1. 程式人生 > 實用技巧 >HashMap原始碼分析

HashMap原始碼分析

final V putVal(int hash, K key, V value, boolean onlyIfAbsent,
               boolean evict) {
    HashMap.Node<K,V>[] tab; HashMap.Node<K,V> p; int n, i;
    // 1.如果table為空或者長度為0,即沒有元素,那麼使用resize()方法擴容
    if ((tab = table) == null || (n = tab.length) == 0)
        n = (tab = resize()).length;
    
// 2.計算插入儲存的陣列索引i,此處計算方法同 1.7 中的indexFor()方法 // 如果陣列為空,即不存在Hash衝突,則直接插入陣列 if ((p = tab[i = (n - 1) & hash]) == null) tab[i] = newNode(hash, key, value, null); // 3.插入時,如果發生Hash衝突,則依次往下判斷 else { HashMap.Node<K,V> e; K k; // a.判斷table[i]的元素的key是否與需要插入的key一樣,若相同則直接用新的value覆蓋掉舊的value
// 判斷原則equals() - 所以需要當key的物件重寫該方法 if (p.hash == hash && ((k = p.key) == key || (key != null && key.equals(k)))) e = p; // b.繼續判斷:需要插入的資料結構是紅黑樹還是連結串列 // 如果是紅黑樹,則直接在樹中插入 or 更新鍵值對 else if (p instanceof HashMap.TreeNode) e
= ((HashMap.TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value); // 如果是連結串列,則在連結串列中插入 or 更新鍵值對 else { // i .遍歷table[i],判斷key是否已存在:採用equals對比當前遍歷結點的key與需要插入資料的key // 如果存在相同的,則直接覆蓋 // ii.遍歷完畢後任務發現上述情況,則直接在連結串列尾部插入資料 // 插入完成後判斷連結串列長度是否 > 8:若是,則把連結串列轉換成紅黑樹 for (int binCount = 0; ; ++binCount) { if ((e = p.next) == null) { p.next = newNode(hash, key, value, null); if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st treeifyBin(tab, hash); break; } if (e.hash == hash && ((k = e.key) == key || (key != null && key.equals(k)))) break; p = e; } } // 對於i 情況的後續操作:發現key已存在,直接用新value覆蓋舊value&返回舊value if (e != null) { // existing mapping for key V oldValue = e.value; if (!onlyIfAbsent || oldValue == null) e.value = value; afterNodeAccess(e); return oldValue; } } ++modCount; // 插入成功後,判斷實際存在的鍵值對數量size > 最大容量 // 如果大於則進行擴容 if (++size > threshold) resize(); // 插入成功時會呼叫的方法(預設實現為空) afterNodeInsertion(evict); return null; }