HashMap源碼之treeifyBin()方法

/**
* tree桶方法.
* 通常用做桶結構變動後,將桶中過長的鏈表轉換爲紅黑樹
*/
final void treeifyBin(Node<K,V>[] tab, int hash) {
    // n - 表明參數tab長度
    // index - tab中表示hash的下標
    // hash - 待處理的鏈表節點hash
    // e - 目標節點
    int n, index; Node<K,V> e;
    // 判斷tab是否爲空或tab長度MIN_TREEIFY_CAPACITY=64
    // 也就是說,在桶中單個鏈表長度可能已經達到要求(如putVal中的binCount >= TREEIFY_THRESHOLD - 1),可是桶容量未達標時,也不會進行tree化
    if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
        // 表是空的或表容量小於MIN_TREEIFY_CAPACITY
        // 重置大小
        resize();
    // 能夠tree化,檢查鏈表節點是否存在
    else if ((e = tab[index = (n - 1) & hash]) != null) {
        // 鏈表節點存在
        
        // 樹節點頭與尾
        TreeNode<K,V> hd = null, tl = null;
        // 已經有第一個目標,直接do while
        do {
            // 構造一個TreeNode.(這裏沒有額外邏輯,僅僅是使用當前的e建立了TreeNode)
            // 注意,這裏的Tree繼承自LinkedHashMap.Entry,內部包含了before與after的雙向鏈表.可是TreeNode又自行實現了雙向鏈表prev與next,並無使用前者的數據結構
            TreeNode<K,V> p = replacementTreeNode(e, null);
            // 判斷尾部是否爲空
            if (tl == null)
                // 初始化頭部
                hd = p;
            else {
                // 尾部不爲空
                // 設置上一個節點
                // 設置尾部下一個節點
                p.prev = tl;
                tl.next = p;
            }
            // 交換尾部
            tl = p;
        } while ((e = e.next) != null);
        
        // 賦值並判斷頭部節點是否爲空
        if ((tab[index] = hd) != null)
            // 調用TreeNode的treeify組裝紅黑樹
            hd.treeify(tab);
    }
}

/**
* 組裝紅黑樹
*/
final void treeify(Node<K,V>[] tab) {
    // 根節點(黑色節點)
    TreeNode<K,V> root = null;
    // 進行迭代.(當前this做用域位於TreeNode實例)
    // x表示當前遍歷中的節點
    for (TreeNode<K,V> x = this, next; x != null; x = next) {
        // 緩存next
        next = (TreeNode<K,V>)x.next;
        // 保證當前節點左右節點爲null
        x.left = x.right = null;
        // 判斷是否存在根節點
        if (root == null) {
            // 不存在
            // 跟節點沒有父級.因此設置爲null
            x.parent = null;
            // 紅黑樹中,根節點是黑色的
            x.red = false;
            // 保存到局部變量
            root = x;
        }
        else {
            // 跟節點已確認
            
            // 緩存key
            K k = x.key;
            // 緩存hash
            int h = x.hash;
            // key類型
            Class<?> kc = null;
            // -------------------- 對跟節點進行遍歷,查找插入位置 --------------------
            // p是插入節點的父節點
            for (TreeNode<K,V> p = root;;) {
                // dir - 用來表達左右.
                // ph - 父節點hash
                int dir, ph;
                // 父節點key
                K pk = p.key;
                
                // -------------------- 判斷插入到左仍是右節點 --------------------
                // 初始化父節點hash
                // 判斷父節點hash是否大於當前節點hash
                if ((ph = p.hash) > h)
                    // dir = -1 插入節點在父節點左側
                    dir = -1;
                // 判斷父節點hash是否小於當前節點hash
                else if (ph < h)
                    // dir = 1 插入節點在父節點右側
                    dir = 1;
                // 父節點hash等於當前節點hash,進行額外的處理
                // 這裏使用了基於Class的一些處理辦法,最終保證了dir的正確值(不爲0) TODO 待補充 
                else if ((kc == null &&
                          (kc = comparableClassFor(k)) == null) ||
                         (dir = compareComparables(kc, k, pk)) == 0)
                    dir = tieBreakOrder(k, pk);


                // -------------------- 獲取左或右節點並進行操做 --------------------
                // 緩存插入節點的父節點
                TreeNode<K,V> xp = p;
                // 使用dir獲取父節點對應的左或右節點,而且檢查這個節點是否爲null.不爲null時,進入下一次循環
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    // 父節點左或右節點爲null
                    
                    // 設置父級節點
                    x.parent = xp;
                    // 再次判斷左右
                    if (dir <= 0)
                        // 將父節點的左子節點複製爲當前節點
                        xp.left = x;
                    else
                        // 將父節點的右子節點複製爲當前節點
                        xp.right = x;
                    // 進行平衡
                    root = balanceInsertion(root, x);
                    // 退出查找插入位置的循環,進行下一個元素的插入
                    break;
                }
            }
        }
    }
    
    // 由於在進行旋轉操做時,可能會修改根節點到其餘節點.致使桶中的直接節點爲分支節點,因此須要進行修正
    moveRootToFront(tab, root);
}
相關文章
相關標籤/搜索