关于Java中的HashMap的深浅拷贝的测试与几点思考

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所有。

时间: 2024-12-20 23:17:32

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

java中的HashMap解析

这篇文章准备从源码的角度带大家分析一下java中的hashMap的原理,在了解源码之前,我们先根据自己的理解创建一个hashMap. 先说明一下创建的具体原理是这样的,所谓hashMap,必然是用hash方法来区分不同的key值.学过hash的都知道,我们解决hash冲突的一种方法就是使用散列和桶,首先确定所在的桶号,然后在桶里面逐个查找.其实我们也可以单纯使用数组实现map,使用散列是为了获得更高的查询效率. 要写自己的hashmap前,必须说明一下两个方法,就是hashcode()和equa

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

Java中关于HashMap的使用和遍历 分类: 算法与数据结构2011-10-19 10:53 5345人阅读 评论(0) 收藏 举报 hashmapjavastringobjectiteratorlist 1:使用HashMap的一个简单例子 [java] view plaincopy package com.pb.collection; import java.util.HashMap; import java.util.Iterator; import java.util.Set; im

javascript 实现类似Java中的hashmap

应用场景: 想把函数和函数对应的数据比较直观的存储起来 突然觉得Javascript中的object不够用了  所以自己仿了个java中的hashmap key支持任何Javascript类型的数据 但是NaN Undefined 这种类型的没有实验 以下是源码 function hashmapfn(){      var p={      rkey:7,      size:0,      obj:{}      };      function getKey(key){      if(ty

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

Java中的阻塞和非阻塞IO包各自的优劣思考 NIO 设计背后的基石:反应器模式,用于事件多路分离和分派的体系结构模式. 反应器(Reactor):用于事件多路分离和分派的体系结构模式 通常的,对一个文件描述符指定的文件或设备, 有两种工作方式: 阻塞 与非阻塞 .所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没有东西可读,或者暂时不可写, 程序就进入等待 状态, 直到有东西可读或者可写为止.而对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等

深入理解Java中的HashMap

HashMap继承自抽象类AbstractMap,抽象类AbstractMap实现了Map接口.关系图如下所示: import java.util.*; public class SimpleMap<K,V> extends AbstractMap<K,V> { //keys存储所有的键 private List<K> keys = new ArrayList<K>(); //values存储所有的值 private List<V> values

深入理解Java中的HashMap的实现原理

HashMap继承自抽象类AbstractMap,抽象类AbstractMap实现了Map接口.关系图例如以下所看到的: Java中的Map<key, value>接口同意我们将一个对象作为key.也就是能够用一个对象作为key去查找还有一个对象. 在我们探讨HashMap的实现原理之前,我们先自己实现了一个SimpleMap类,该类继承自AbstractMap类. 详细实现例如以下: import java.util.*; public class SimpleMap<K,V>

python中的“赋值与深浅拷贝”

Python中,赋值与拷贝(深/浅拷贝)之间是有差异的,这主要源于数据在内存中的存放问题,本文将对此加以探讨. 1 赋值(添加名字) 赋值不会改变内存中数据存放状态,比如在内存中存在一个名为data的数据,此时若执行语句data_01 = data,则现在该份数据有了两个名称(data和data_01),其余都不发生改变,使用任何一个名称对数据进行操作,那么用另外一个名称拿数据时,数据会呈现之间发生的改变.示例如下: 图中,给列表分配了两个名称a与b,对a做改变时b会同样改变,对b做改变时a也会

浅析java中的hashMap

?  HashMap 和 HashSet 是 Java Collection Framework 的两个重要成员,其中 HashMap 是 Map 接口的常用实现类,HashSet 是 Set 接口的常用实现类.虽然 HashMap 和 HashSet 实现的接口规范不同,但它们底层的 Hash 存储机制完全一样,甚至 HashSet 本身就采用 HashMap 来实现的. 通过 HashMap.HashSet 的源代码分析其 Hash 存储机制集合和引用 就像引用类型的数组一样,当我们把 Ja

Java中的HashMap和HashTable到底哪不同?

学习Java的同学注意了!!! 学习过程中遇到什么问题或者想获取学习资源的话,欢迎加入Java学习交流群,群号码:456544752  我们一起学Java! HashMap和HashTable有什么不同?在面试和被面试的过程中,我问过也被问过这个问题,也见过了不少回答,今天决定写一写自己心目中的理想答案. 代码版本 JDK每一版本都在改进.本文讨论的HashMap和HashTable基于JDK 1.7.0_67.源码见这里 1. 时间 HashTable产生于JDK 1.1,而HashMap产生