0、前言

工作忙起来后,许久不看算法,竟然DFA敏感词算法都要看好一阵才能理解。。。真是和三阶魔方还原手法一样,田园将芜,非常可惜啊。

在DFA算法中,第一步是需要理解它的数据结构,在此基础上,涉及到一些Hashmap的赋值。这里的赋值非常有趣,三个Hashmap翻来覆去赋值,就解决了敏感词表的初始化。

里面都是属于下文中的Hashmap“浅拷贝”,那么究竟Java中的Hashmap有哪些拷贝方法呢?

1、测试代码

HashMap hm_source = new HashMap();
HashMap hm_clone = new HashMap();
hm_source.put("1", "1"); // hashmap deep clone method 1
hm_clone = (HashMap)hm_source.clone();
// hashmap deep clone method 2
hm_clone.putAll(hm_source);// hashmap shadow clone
// hm_b = hm_a; hm_source.put("2", "2");
System.out.println("hm_source增加元素后,hm_source:"+hm_source);
System.out.println("hm_source增加元素后,hm_clone:"+hm_clone); System.out.println("是否指向同一内存地址:"+(hm_source==hm_clone));
System.out.println("第一个元素是否指向同一内存地址:"+(hm_source.get(1)==hm_clone.get(1)));

上面介绍了两种Hashmap深拷贝的方法,分别是hashmap.clone()和hashmap.putAll(hm_source),效果一样,输出如下:

hm_source增加元素后,hm_source:{1=1, 2=2}
hm_source增加元素后,hm_clone:{1=1}
是否指向同一内存地址:false
第一个元素是否指向同一内存地址:true

那么浅拷贝呢?(代码中注释的那段,直接等号=赋值),输出如下:

hm_source增加元素后,hm_source:{1=1, 2=2}
hm_source增加元素后,hm_clone:{1=1, 2=2}
是否指向同一内存地址:true
第一个元素是否指向同一内存地址:true

2、输出解析

不难发现,深浅拷贝确实如其名,

深拷贝:两个Hashmap对象似乎彻底无关,互相增加修改元素后,都不影响对方;

浅拷贝:两个Hashmap对象就是“软链接ln”,互相牵制,你改动了,我也跟着变。

3、上述猜想是否正确?

我党的思想路线是实事求是,预想剖析根本区别,大家可以看看JDK clone函数的源码。

但是从现象我们得到的初步结论有几点:

  1. 浅拷贝的两个对象使用的内存地址相同,深拷贝的对象地址“另立门户”;
  2. 深拷贝的两个对象也不完全“彻底无关”,仅仅是复制了元素的引用;

关于结论3.2,我也是在 HashMap的clone方法 博文中学习到了,里面使用的Hashmap元素是Bean类型的,深拷贝下的元素修改,也会“打断骨头连着筋”地让两个Hashmap同时更新。

但是仅限于“元素修改”,如若“元素增删”,那两个Hashmap对象就“翻脸不认人”了,不会同步更新。

4、hashmap.clone()

JDK是个难得的学习材料,源码还是要读的。现在也粘贴过来,做一些记录。

/**
* Returns a shallow copy of this <tt>HashMap</tt> instance: the keys and
* values themselves are not cloned.
* 【咱们中文叫“深拷贝”,老外美其名曰“拷贝一份实例的'浅拷贝'”,更加严谨】
* @return a shallow copy of this map
*/
@SuppressWarnings("unchecked")
@Override
public Object clone() {
HashMap<K,V> result;
try {
result = (HashMap<K,V>)super.clone();
} catch (CloneNotSupportedException e) {
// this shouldn't happen, since we are Cloneable
throw new InternalError(e);
}
result.reinitialize();
result.putMapEntries(this, false);
return result;
}
/**
* Implements Map.putAll and Map constructor
*
* @param m the map
* @param evict false when initially constructing this map, else
* true (relayed to method afterNodeInsertion).
*/
final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
int s = m.size();
if (s > 0) {
if (table == null) { // pre-size
float ft = ((float)s / loadFactor) + 1.0F;
int t = ((ft < (float)MAXIMUM_CAPACITY) ?
(int)ft : MAXIMUM_CAPACITY);
if (t > threshold)
threshold = tableSizeFor(t);
}
else if (s > threshold)
resize();
for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
K key = e.getKey();
V value = e.getValue();
//【putVal方法里面我初步扫了一下,也未涉及Hashmap instance对象的新建,是一些Hashmap结构中的Node的新建】
putVal(hash(key), key, value, false, evict);
}
}
}

以代码最终解释权由JDK1.8.x所有。

关于Java中的HashMap的深浅拷贝的测试与几点思考的更多相关文章

  1. 沉淀再出发:java中的HashMap、ConcurrentHashMap和Hashtable的认识

    沉淀再出发:java中的HashMap.ConcurrentHashMap和Hashtable的认识 一.前言 很多知识在学习或者使用了之后总是会忘记的,但是如果把这些只是背后的原理理解了,并且记忆下 ...

  2. java 中遍历hashmap 和hashset 的方法

    一.java中遍历hashmap:    for (Map.Entry<String, Integer> entry : tempMap.entrySet()) {     String ...

  3. Java中关于HashMap的元素遍历的顺序问题

    Java中关于HashMap的元素遍历的顺序问题 今天在使用如下的方式遍历HashMap里面的元素时 1 for (Entry<String, String> entry : hashMa ...

  4. Java中关于HashMap的使用和遍历(转)

    Java中关于HashMap的使用和遍历 分类: 算法与数据结构2011-10-19 10:53 5345人阅读 评论(0) 收藏 举报 hashmapjavastringobjectiterator ...

  5. [转]为什么Java中的HashMap默认加载因子是0.75

    前几天在一个群里看到有人讨论hashmap中的加载因子为什么是默认0.75. HashMap源码中的加载因子 static final float DEFAULT_LOAD_FACTOR = 0.75 ...

  6. Java中的HashMap的工作原理是什么?

    问答题23 /120 Java中的HashMap的工作原理是什么? 参考答案 Java中的HashMap是以键值对(key-value)的形式存储元素的.HashMap需要一个hash函数,它使用ha ...

  7. JAVA中JavaBean对象之间属性拷贝的方法

    JAVA中JavaBean对象之间的拷贝通常是用get/set方法,但如果你有两个属性相同的JavaBean或有大部分属性相同的JavaBean,对于这种情况,可以采用以下几个简便方法处理. 下面对这 ...

  8. Java中的阻塞和非阻塞IO包各自的优劣思考(经典)

    Java中的阻塞和非阻塞IO包各自的优劣思考 NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式. 反应器(Reactor):用于事件多路分离和分派的体系结构模式 通常的,对一个 ...

  9. hash表及Java中的HashMap与HashSet

    链接: http://alex09.iteye.com/blog/539545/ 当程序试图将一个 key-value 对放入 HashMap 中时,程序首先根据该 key 的 hashCode() ...

随机推荐

  1. getLastSql()用法

    getLastSql()用法 $User = M("User"); // 实例化User对象 $User->find(1); echo $User->getLastSq ...

  2. couldn't find "libstlport_shared.so"

    SUPPORTED_64_BIT_ABIS=[Ljava.lang.String;@9341bd4 BOARD=GEM-703LT BOOTLOADER=unknown TYPE=user match ...

  3. Android 4.4 KitKat 支持 u 盘功能

    Android U 盘功能实现和分析 u 盘功能实现结果: u 盘会当成 usb storage 在 Settings Storage 里面显示. 准备工作 内核需支持 usb host,需支持 FU ...

  4. STRUTS2配置动态页面

      STRUTS2配置动态页面 CreateTime--2017年5月11日09:00:31Author:Marydon 1.struts配置 <?xml version="1.0&q ...

  5. Android系统示例分析之AccelerometerPlay

    程序界面: 创建 SimulationView , 实现接口 SensorEventListener , 实现接口中两个抽象方法 public void onSensorChanged(SensorE ...

  6. Python 字符串与数字拼接报错

    Python 不像 JS 或者 PHP 这种弱类型语言里在字符串连接时会自动转换类型,而是直接报错. 如: 上述是Python 字符串与数字拼接报错,解决办法是:使用bytes函数把int型转换为st ...

  7. mybatis实战教程(mybatis in action)之三:实现数据的增删改查

    前面已经讲到用接口的方式编程.如果不一致就会出错,这一章主要在上一讲基于接口编程的基础上完成如下事情:1. 用 mybatis 查询数据,包括列表2. 用 mybatis 增加数据3. 用 mybat ...

  8. web.xml文件书写规则

    在为class文件写xml配置文件的书写规则,需要书写的东西如下 <servlet> <servlet-name></servlet-name> <servl ...

  9. Mysql User表权限字段说明全介绍

    一:mysql权限表user字段详解: Select_priv.确定用户是否可以通过SELECT命令选择数据. Insert_priv.确定用户是否可以通过INSERT命令插入数据. Update_p ...

  10. Docker中images中none的镜像删除

    docker build 或是 pull 命令就会产生临时镜像.如果我们用dockerfile创建一个helloworld镜像后,因为版本更新需要重新创建,那么以前那个版本的镜像就会 成为临时镜像.这 ...