關於Java中的HashMap的深淺拷貝的測試與幾點思考

0、前言

工做忙起來後,許久不看算法,居然DFA敏感詞算法都要看好一陣才能理解。。。真是和三階魔方還原手法同樣,田園將蕪,很是惋惜啊。算法

在DFA算法中,第一步是須要理解它的數據結構,在此基礎上,涉及到一些Hashmap的賦值。這裏的賦值很是有趣,三個Hashmap翻來覆去賦值,就解決了敏感詞表的初始化。數據結構

裏面都是屬於下文中的Hashmap「淺拷貝」,那麼究竟Java中的Hashmap有哪些拷貝方法呢?app

一、測試代碼

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),效果同樣,輸出以下:ide

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

二、輸出解析

不難發現,深淺拷貝確實如其名,學習

深拷貝:兩個Hashmap對象彷佛完全無關,互相增長修改元素後,都不影響對方;測試

淺拷貝:兩個Hashmap對象就是「軟連接ln」,互相牽制,你改動了,我也跟着變。this

三、上述猜測是否正確?

我黨的思想路線是實事求是,預想剖析根本區別,你們能夠看看JDK clone函數的源碼。spa

可是從現象咱們獲得的初步結論有幾點:.net

  1. 淺拷貝的兩個對象使用的內存地址相同,深拷貝的對象地址「另立門戶」;
  2. 深拷貝的兩個對象也不徹底「完全無關」,僅僅是複製了元素的引用;

關於結論3.2,我也是在 HashMap的clone方法 博文中學習到了,裏面使用的Hashmap元素是Bean類型的,深拷貝下的元素修改,也會「打斷骨頭連着筋」地讓兩個Hashmap同時更新。

可是僅限於「元素修改」,如若「元素增刪」,那兩個Hashmap對象就「翻臉不認人」了,不會同步更新。

四、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全部。

相關文章
相關標籤/搜索