Java基礎:HashMap中putAll方法的疑惑


  最近回顧了下HashMap的源碼(JDK1.7),當讀到putAll方法時,發現了之前寫的TODO標記,當時由於時間匆忙沒來得及深究,現在回顧到了就再仔細思考了下

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        int numKeysToBeAdded = m.size();
        if (numKeysToBeAdded == 0)
            return;
        // TODO 這里的numKeysToBeAdded是不是應該要this.size+m.size()呢?
        // TODO 這里確實有點問題,下面的for循環中put操作可能會導致再次resize,奇怪怎么沒人提出這個問題呢?
        if (numKeysToBeAdded > threshold) {
            // +1是為了補上被強轉為int而抹去的小數部分
            int targetCapacity = (int)(numKeysToBeAdded / loadFactor + 1);
            if (targetCapacity > MAXIMUM_CAPACITY)
                targetCapacity = MAXIMUM_CAPACITY;
            int newCapacity = table.length;
            while (newCapacity < targetCapacity)
                newCapacity <<= 1;
            if (newCapacity > table.length)
                resize(newCapacity);
        }

        for (Map.Entry<? extends K, ? extends V> e : m.entrySet())
            put(e.getKey(), e.getValue());
    }

   如注釋中所示 numKeysToBeAdded > threshold 就是想提前判斷Map是否需要擴容,如果需要的話則直接一步到位,從而防止循環中的put操作引起多次擴容,以次來減小 resize 方法帶來的性能開銷。

但是:我們看方法的第一行,int numKeysToBeAdded = m.size();  如果要實現擴容一步到位的話,這里的 numKeysToBeAdded 不應該是當前Map的size加m的size之和嗎?  this.size + m.size()  > threshold

就擴容才能保證m中所有元素添加到當前HashMap后只觸發一次resize 。

  測試代碼如下,直接debug HashMap的putAll方法,我們可以看到整個putAll是進行了兩次resize

        Map map = new HashMap(4);
        Map m = new HashMap(8);
        map.put("a", "haha");
        map.put("b", "haha");
        map.put("c", "haha");
        m.put("1", "a");
        m.put("2", "a");
        m.put("3", "a");
        m.put("4", "a");
        map.putAll(m);

   JDK1.8的HashMap已經實現已經做了很大的修改,但是當我切換到1.8 debug時還是resize了兩次,為什么呢?仔細看下面的注釋(當時看源碼的時候直接把這段注釋忽略了,汗),JDK的大神們給出了如下的解釋,顯然他們也知道這個問題,但是主要考慮到m和當前的HashMap中可能存在重復的key,這樣的話就可能造成HashMap浪費了比較大的空間(畫外音:HashMap默認加載因子為0.75的設計初衷不就是采取了空間換時間的思想嚒??)

        /*
         * Expand the map if the map if the number of mappings to be added
         * is greater than or equal to threshold.  This is conservative; the
         * obvious condition is (m.size() + size) >= threshold, but this
         * condition could result in a map with twice the appropriate capacity,
         * if the keys to be added overlap with the keys already in this map.
         * By using the conservative calculation, we subject ourself
         * to at most one extra resize.
         */

在HashMap中 size  肯定會小於或等於 threshold ,所以putAll時當 m.size() > threshold 進行擴容,HashMap的容量增加至少1倍,則因為存在 m.size() > size 所以就算 m.size() + size > threshold(第一次擴容后) 只要再做一次擴容就可以滿足HashMap的規則了。

更全的學習注釋可以參考:https://github.com/hiccup234/misc/blob/master/src/main/java/top/hiccup/jdk/container/mycontainer/MyHashMap7.java

 


免責聲明!

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



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