一、概述
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); // 單獨解析
}