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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM