JDK8:HashMap源碼解析:TreeNode類的treeify方法

一、概述

treeify方法是TreeNode類的一個實例方法,通過TreeNode對象調用,實現該對象打頭的鏈表轉換爲樹結構。

 

二、方法解析

/**
 * 參數爲HashMap的元素數組
 */
final void treeify(Node<K,V>[] tab) {
    TreeNode<K,V> root = null; // 定義樹的根節點
    for (TreeNode<K,V> x = this, next; x != null; x = next) { // 遍歷鏈表,x指向當前節點、next指向下一個節點
        next = (TreeNode<K,V>)x.next; // 下一個節點
        x.left = x.right = null; // 設置當前節點的左右節點爲空
        if (root == null) { // 如果還沒有根節點
            x.parent = null; // 當前節點的父節點設爲空
            x.red = false; // 當前節點的紅色屬性設爲false(把當前節點設爲黑色)
            root = x; // 根節點指向到當前節點
        }
        else { // 如果已經存在根節點了
            K k = x.key; // 取得當前鏈表節點的key
            int h = x.hash; // 取得當前鏈表節點的hash值
            Class<?> kc = null; // 定義key所屬的Class
            for (TreeNode<K,V> p = root;;) { // 從根節點開始遍歷,此遍歷沒有設置邊界,只能從內部跳出
                // GOTO1
                int dir, ph; // dir 標識方向(左右)、ph標識當前樹節點的hash值
                K pk = p.key; // 當前樹節點的key
                if ((ph = p.hash) > h) // 如果當前樹節點hash值 大於 當前鏈表節點的hash值
                    dir = -1; // 標識當前鏈表節點會放到當前樹節點的左側
                else if (ph < h)
                    dir = 1; // 右側

                /*
                 * 如果兩個節點的key的hash值相等,那麼還要通過其他方式再進行比較
                 * 如果當前鏈表節點的key實現了comparable接口,並且當前樹節點和鏈表節點是相同Class的實例,那麼通過comparable的方式再比較兩者。
                 * 如果還是相等,最後再通過tieBreakOrder比較一次
                 */
                else if ((kc == null &&
                            (kc = comparableClassFor(k)) == null) ||
                            (dir = compareComparables(kc, k, pk)) == 0)
                    dir = tieBreakOrder(k, pk);

                TreeNode<K,V> xp = p; // 保存當前樹節點

                /*
                 * 如果dir 小於等於0 : 當前鏈表節點一定放置在當前樹節點的左側,但不一定是該樹節點的左孩子,也可能是左孩子的右孩子 或者 更深層次的節點。
                 * 如果dir 大於0 : 當前鏈表節點一定放置在當前樹節點的右側,但不一定是該樹節點的右孩子,也可能是右孩子的左孩子 或者 更深層次的節點。
                 * 如果當前樹節點不是葉子節點,那麼最終會以當前樹節點的左孩子或者右孩子 爲 起始節點  再從GOTO1 處開始 重新尋找自己(當前鏈表節點)的位置
                 * 如果當前樹節點就是葉子節點,那麼根據dir的值,就可以把當前鏈表節點掛載到當前樹節點的左或者右側了。
                 * 掛載之後,還需要重新把樹進行平衡。平衡之後,就可以針對下一個鏈表節點進行處理了。
                 */
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    x.parent = xp; // 當前鏈表節點 作爲 當前樹節點的子節點
                    if (dir <= 0)
                        xp.left = x; // 作爲左孩子
                    else
                        xp.right = x; // 作爲右孩子
                    root = balanceInsertion(root, x); // 重新平衡
                    break;
                }
            }
        }
    }

    // 把所有的鏈表節點都遍歷完之後,最終構造出來的樹可能經歷多個平衡操作,根節點目前到底是鏈表的哪一個節點是不確定的
    // 因爲我們要基於樹來做查找,所以就應該把 tab[N] 得到的對象一定根節點對象,而目前只是鏈表的第一個節點對象,所以要做相應的處理。
    moveRootToFront(tab, root); // 單獨解析
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章