HashMap源碼分析 —— put與get(四)

HashMap源碼分析 —— put與get(四)

鏈接

上一節(jié) : HashMap源碼分析 —— put與get(三)

2.6 resize()擴(kuò)容操作

    Node<K,V> loHead = null, loTail = null;
    Node<K,V> hiHead = null, hiTail = null;
    Node<K,V> next;
    do {
        next = e.next;
        // 還是原來(lái)的索引值
        if ((e.hash & oldCap) == 0) {
            if (loTail == null)
                loHead = e;
            else
                loTail.next = e;
            loTail = e;
        }
        // 不是原來(lái)的索引值了 需要遷移
        else {
            if (hiTail == null)
                hiHead = e;
            else
                hiTail.next = e;
            hiTail = e;
        }
    } while ((e = next) != null);
    if (loTail != null) {
      loTail.next = null;
      newTab[j] = loHead;
    }
    if (hiTail != null) {
      hiTail.next = null;
      newTab[j + oldCap] = hiHead;
    }

不難看出,loHeadloTail兩個(gè)節(jié)點(diǎn)分別記錄不需要移動(dòng)的鏈表的頭部和尾部,hiHeadhiTail分別記錄需要移動(dòng)的鏈表頭部和尾部.

假設(shè)在擴(kuò)容的時(shí)候某個(gè)數(shù)組下有這樣一個(gè)鏈表 :

image

其中,假設(shè)天藍(lán)色部分的不需要挪動(dòng),紅色部分的需要挪動(dòng)

第一步 : 建立loHead loTail hiHead hiTail四個(gè)節(jié)點(diǎn)

第二步 :

image

第三步 :

image

...

第N步 :

image

最后一步 :

把以loHead為首的鏈表放到數(shù)組的原位置,把以hiHead為首的鏈表放到原位置+oldCap的位置,這就是鏈表的擴(kuò)容操作

下面圖片是美團(tuán)技術(shù)團(tuán)隊(duì)的put函數(shù)的流程總結(jié) :

image

2.7 腳步加快 —— get()函數(shù)

相對(duì)于put()函數(shù) get()函數(shù)要簡(jiǎn)單的多 :

    public V get(Object key) {
        Node<K,V> e;
        return (e = getNode(hash(key), key)) == null ? null : e.value;
    }
    final Node<K,V> getNode(int hash, Object key) {
        Node<K,V>[] tab; Node<K,V> first, e; int n; K k;
        if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
            if (first.hash == hash && // always check first node
                ((k = first.key) == key || (key != null && key.equals(k))))
                return first;
            if ((e = first.next) != null) {
                if (first instanceof TreeNode)
                    return ((TreeNode<K,V>)first).getTreeNode(hash, key);
                do {
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        return e;
                } while ((e = e.next) != null);
            }
        }
        return null;
    }

首先看下條件 :

// 數(shù)組不能是空的 數(shù)組的長(zhǎng)度必須要大于0 數(shù)組當(dāng)前索引有節(jié)點(diǎn)存在
if ((tab = table) != null && (n = tab.length) > 0 &&
            (first = tab[(n - 1) & hash]) != null) {
    // ...           
}

然后,如果第一個(gè)節(jié)點(diǎn)正好就是要找的,那就直接返回吧 :

if (first.hash == hash && // always check first node
    ((k = first.key) == key || (key != null && key.equals(k))))
    return first;
if ((e = first.next) != null) {
    // 如果是紅黑樹(shù)
    if (first instanceof TreeNode)
        return ((TreeNode<K,V>)first).getTreeNode(hash, key);
   // 如果是鏈表 就循環(huán) 直到最后一個(gè)節(jié)點(diǎn)
   do {
       if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k))))
           return e;
    } while ((e = e.next) != null);

}

2.8 HashMap的最后一個(gè)構(gòu)造函數(shù)

最后一個(gè)構(gòu)造函數(shù)如下 :

public HashMap(Map<? extends K, ? extends V> m) {
   // 加載因子直接設(shè)置成默認(rèn)的
   this.loadFactor = DEFAULT_LOAD_FACTOR;
   putMapEntries(m, false);
}

注意形式參數(shù)中的泛型

    final void putMapEntries(Map<? extends K, ? extends V> m, boolean evict) {
        int s = m.size();
        if (s > 0) {
            // 其實(shí)構(gòu)造函數(shù)時(shí) table是null
            if (table == null) { // pre-size
                // 初始化數(shù)據(jù)的容量
                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();
            // 遍歷要插入的map中的每一個(gè)節(jié)點(diǎn) 并把它們插入到調(diào)用它的map中
            for (Map.Entry<? extends K, ? extends V> e : m.entrySet()) {
                K key = e.getKey();
                V value = e.getValue();
                putVal(hash(key), key, value, false, evict);
            }
        }
    }

其實(shí)除了構(gòu)造器中調(diào)用了putMapEntries函數(shù)之外,還有putAll()函數(shù)調(diào)用了它

下一節(jié) : HashMap源碼分析 —— put與get(總)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書(shū)系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

友情鏈接更多精彩內(nèi)容