HashMap put方法的源碼分析

背景知識:

java1.7 HashMap用的是數組+鏈表實現的,同時採用的頭插入法,存在死循環的問題

java1.8 HashMap用的是數組+鏈表+紅黑樹實現的,採用尾插法實現的,解決了死循環的問題,今天分析的就是1.8

 

    // 初始容量爲16
    static final int DEFAULT_INITIAL_CAPACITY = 1 << 4; // aka 16    

    // 默認負載因子    
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    // 默認臨界值
    static final int TREEIFY_THRESHOLD = 8;

    // 此爲爲我們調用的put方法
    public V put(K key, V value) {
        return putVal(hash(key), key, value, false, true);
    }
   
    // 獲取key對應的hash值的方法
    static final int hash(Object key) {
        int h;
        // 此處可以看出,hashmap會把爲null的key方法數組下標爲0的位置上
        return (key == null) ? 0 : (h = key.hashCode()) ^ (h >>> 16);
    }

     /**
     * Implements Map.put and related methods.
     *
     * @param  key的hash值
     * @param  key
     * @param  key對應的value
     * @param  默認flase,會覆蓋相同key的value值
     * @param  默認true,初始化不調用此方法
     * @return previous value, or null if none
     */
    final V putVal(int hash, K key, V value, boolean onlyIfAbsent,boolean evict) {
        // tab爲數組,p爲鏈表的臨時節點,n爲數組的長度,i爲key對應的數組index
        Node<K,V>[] tab; Node<K,V> p; int n, i;
        // 判斷數組是否爲空,直接擴容
        if ((tab = table) == null || (n = tab.length) == 0){
            n = (tab = resize()).length;
        }
        // 根絕key的hash值獲取,key對應的數組下標,如果該下標中,數組沒有值,則直接放進去
        if ((p = tab[i = (n - 1) & hash]) == null){
            tab[i] = newNode(hash, key, value, null);
        }
        // 否則發生了hash衝突(就是根據放入的key的hash值算出下標位置,在數組中已經存有數據了)
        else {
            // 新實例化一個node節點,用於暫存數據
            Node<K,V> e; K k;
            // hash值和key的都相等,則覆蓋此key的value值
            if (p.hash == hash &&((k = p.key) == key || (key != null && key.equals(k))))
                e = p;
            // 如果此時,p爲樹節點,則將紅黑樹中key對應的節點取出來,給e
            // instancesof() 判斷左邊的是否屬於右邊的類
            else if (p instanceof TreeNode)
                e = ((TreeNode<K,V>)p).putTreeVal(this, tab, hash, key, value);
            // 此時爲同一個數組下標,但是key值不等
            else {
                // 遍歷鏈表
                for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        // 遍歷到鏈表尾部,添加新的節點
                        p.next = newNode(hash, key, value, null);
                        // 添加新的節點之後,如果鏈表長度 > 8,則轉換爲紅黑樹
                        if (binCount >= TREEIFY_THRESHOLD - 1) 
                            treeifyBin(tab, hash);
                        break;
                    }
                    // 找到要覆蓋的節點,直接跳出遍歷
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
            }
            // 如果此時e不爲空,則說明,此key在hashmap中已經存在,進行覆蓋操作
            if (e != null) { 
                V oldValue = e.value;
                if (!onlyIfAbsent || oldValue == null)              
                    e.value = value;
                afterNodeAccess(e);
                // 返回舊value
                return oldValue;
            }
        }
        // 記錄hashmap變化的次數
        ++modCount;

        // 如果數組的長度大於臨界值,則hashmap的數組進行擴容
        if (++size > threshold)
            resize();
        // 這是一個空實現的函數,用作LinkedHashMap重寫使用。
        afterNodeInsertion(evict);
        return null;
    }
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章