HashMap源碼之resize()方法

/**
* 這個方法是基於當前桶中全部元素的數量進行計算的使用閾值爲threshold.它不一樣於鏈表轉化到tree時的鏈表長度(能夠理解爲樹的高度)閾值TREEIFY_THRESHOLD
*/
final Node<K,V>[] resize() {
    //----------------------------------- 新容量與閾值計算 -----------------------------------
    
    // 緩存桶引用
    Node<K,V>[] oldTab = table;
    // 緩存老的桶的長度,桶爲null時,使用0
    // 注意,這裏用的是oldTab.length,而不是size
    int oldCap = (oldTab == null) ? 0 : oldTab.length;
    // 緩存閾值
    int oldThr = threshold;
    // 新桶容量與閾值
    int newCap, newThr = 0;
    
    // 老容量大於.這通常表明這個桶已經通過了resize的數次處理
    if (oldCap > 0) {
        
        // 老容量大於MAXIMUM_CAPACITY = 1 << 30 = 1073741824
        // 容量計算方式爲n<<1,當oldCap >= MAXIMUM_CAPACITY時,再次執行位移.其可能的最大值就是Integer.MAX_VALUE
        if (oldCap >= MAXIMUM_CAPACITY) {
            // 設置閾值爲Integer.MAX_VALUE
            threshold = Integer.MAX_VALUE;
            // 直接return.放棄所有後續處理
            return oldTab;
        }
        // 使用oldCap << 1初始化newCap
        // 當oldCap小於MAXIMUM_CAPACITY而且oldCap大於DEFAULT_INITIAL_CAPACITY(16)時
        // 此時newCap可能已經大於MAXIMUM_CAPACITY而且newThr=0或者newCap很小(小於16>>2)而且newThr=0
        else if ((newCap = oldCap << 1) < MAXIMUM_CAPACITY &&
                 oldCap >= DEFAULT_INITIAL_CAPACITY)
            //設置newThr爲oldThr << 1(這裏沒有作正確性校驗,待查)
            newThr = oldThr << 1; // double threshold
    }
    // 判斷老閾值是否大於0
    // 走到這說明oldCap==0,而且使用了包含initialCapacity參數的構造器構造了這個map,且沒有被添加過元素
    else if (oldThr > 0) // initial capacity was placed in threshold
        // 使用將新容量複製爲老閾值(newCap此時爲0)
        // 注意: 在使用了包含initialCapacity參數的構造方法時,其threshold已經被計算爲2的n次冪
        newCap = oldThr;
    else {               // zero initial threshold signifies using defaults
        // 默認方法,當使用無參構造方法時,會出現oldThr與oldCap都等於0的狀況
        // 使用默認初始化容量賦值到newCap
        newCap = DEFAULT_INITIAL_CAPACITY;
        // 使用默認初始化容量與加載因子相乘賦值到newThr
        newThr = (int)(DEFAULT_LOAD_FACTOR * DEFAULT_INITIAL_CAPACITY);
    }
    
    // 統一處理newThr
    if (newThr == 0) {
        // 新容量與加載因子相乘
        float ft = (float)newCap * loadFactor;
        // 當newCap與ft均小於MAXIMUM_CAPACITY時,newThr=ft.不然newThr=Integer.MAX_VALUE
        newThr = (newCap < MAXIMUM_CAPACITY && ft < (float)MAXIMUM_CAPACITY ?
                  (int)ft : Integer.MAX_VALUE);
    }
    
    //----------------------------------- 元素重排 -----------------------------------
    // 更新threshold
    threshold = newThr;
    @SuppressWarnings({"rawtypes","unchecked"})
    // 更新桶對象(此時是空的)
    Node<K,V>[] newTab = (Node<K,V>[])new Node[newCap];
    table = newTab;
    // 判斷老桶是否爲空
    if (oldTab != null) {
        // 老桶不爲空.進行遍歷
        for (int j = 0; j < oldCap; ++j) {
            // 桶元素
            Node<K,V> e;
            // 進行桶元素獲取
            // 判斷桶元素是否存在(由於使用(n-1)&hash的方式進行計算,因此常常會出現這種狀況)
            if ((e = oldTab[j]) != null) {
                // 刪除引用
                oldTab[j] = null;
                // 判斷桶元素是否有下一個元素
                if (e.next == null)
                    // 沒有下一個元需.使用相同的算法計算在新桶中的下標並賦值
                    newTab[e.hash & (newCap - 1)] = e;
                // 桶元素存在next,判斷是否爲TreeNode
                else if (e instanceof TreeNode)
                    // 進行委派執行
                    ((TreeNode<K,V>)e).split(this, newTab, j, oldCap);
                else { // preserve order
                    // 對於鏈表結構,拆分到高位與低位兩組
                    
                    // loHead與loTail非別表明低位頭與低位尾
                    Node<K,V> loHead = null, loTail = null;
                    // hiHead與hiTail非別表明高位頭與高位尾
                    Node<K,V> hiHead = null, hiTail = null;
                    // next
                    Node<K,V> next;
                    // 已經存在遍歷目標,直接使用do while
                    do {
                        // 拿到e的next.
                        next = e.next;
                        // 判斷e的hash是不是高位
                        // 判斷原理以下.
                        // 首先oldCap恆定爲2的n次冪,二進制表達爲1000...
                        // 下標計算方程爲(n-1)&hash
                        // 帶入n後,爲...111&hash
                        // 當n=111時,hash爲1101,結果爲101
                        // 當n=1111時,hash爲1101,結果爲1101.表示爲高位(注意hash的高位)
                        // 當n=1111時,hash爲0101,結果爲101.表示爲低位(注意hash的高位)
                        // 這樣就,能夠直接求出新的下標.可是,這種方式須要對全部的元素進行從新計算,很是低效
                        // 因此jdk使用了一個特別的方法.就是直接比較最高位,當一個hash與數組長度(也就是n的n次冪)時,如1101&1000
                        // 當結果等於0時,表明這個hash是低位hash,其餘就是高位hash
                        if ((e.hash & oldCap) == 0) {
                            // 低位
                            // 判斷低位尾部是否存在
                            if (loTail == null)
                                // 不存在,表明頭部也沒有,進行初始化
                                loHead = e;
                            else
                                // 存在,追加到尾部的next
                                loTail.next = e;
                            // 更新尾部
                            loTail = e;
                        }
                        else {
                            // 高位
                            if (hiTail == null)
                                // 不存在,表明頭部也沒有,進行初始化
                                hiHead = e;
                            else
                                // 存在,追加到尾部的next
                                hiTail.next = e;
                            // 更新尾部
                            hiTail = e;
                        }
                    } while ((e = next) != null);
                    
                    // 進行收尾處理
                    // 判斷低位是否爲空
                    if (loTail != null) {
                        // 不爲空
                        // 清除末尾元素的next.當loTail是鏈表倒數第二個元素且倒數第一個元素是高位元素時,須要清空loTail的next對高位元素的引用
                        loTail.next = null;
                        // 低位使用原下標進行保存
                        newTab[j] = loHead;
                    }
                    if (hiTail != null) {
                        // 不爲空
                        // 清除末尾元素的next.理由同上但判斷方式相反
                        hiTail.next = null;
                        // 低位使用原下標+原容量進行保存
                        newTab[j + oldCap] = hiHead;
                    }
                }
            }
        }
    }
    // 返回newTab
    return newTab;
}
相關文章
相關標籤/搜索