HashMap中的TreeNode,紅黑樹源碼分析


在看HashMap的源碼時候看到了TreeNode。因此需要對其進行一個了解。是一個紅黑樹。可以百度一下紅黑樹的數據結構。分析了下源碼,還是比較枯燥的

 

紅黑樹的性質:本身是一個二叉查找樹(所有左節點的值都比右節點的小)。另:

  1. 節點是紅色或者黑色
  2. 根節點是黑色
  3. 每個葉節點(Nil節點,空節點)是黑色的
  4. 每個紅節點對應的兩個子節點都是黑色的(不可能有兩個相連的紅節點)。
  5. 從任意節點出發,到每個葉子節點都有相同的黑色節點。

這保證了紅黑數是平衡的,從根到葉子的最長的可能路徑不多於最短的可能路徑的兩倍長,因此插入、刪除查找的最壞情況是有所保證的,與樹的高度成正比。原因有兩點:

  1. 最短的可能路徑都是黑色節點。
  2. 最長的路徑是紅黑節點交替的路徑。

因為從同一節點出發,到每一個葉子有相同的黑色節點,所以保證了最長路徑是最短路徑的兩倍長。

一、成員變量與構造函數

 1  static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V> {
 2         //父節點
 3         HashMap.TreeNode<K,V> parent;  // red-black tree links
 4         //左節點
 5         HashMap.TreeNode<K,V> left;
 6         //右節點
 7         HashMap.TreeNode<K,V> right;
 8         //用來刪除下一個節點用的,因此prev也就是上一個節點
 9         HashMap.TreeNode<K,V> prev;    // needed to unlink next upon deletion
10         //節點是否為紅色
11         boolean red;
12         TreeNode(int hash, K key, V val, HashMap.Node<K,V> next) {
13             super(hash, key, val, next);
14         }
15 }

 

二、Function root

 1 /**
 2          * 返回包含此節點的樹的根節點
 3          */
 4         final HashMap.TreeNode<K,V> root() {
 5             //定義兩個TreeNode,一個是父節點指針,指向當前節點的parent,所以功能很明顯了。就是遍歷直到頭節點
 6             for (HashMap.TreeNode<K,V> r = this, p;;) {
 7                 if ((p = r.parent) == null)
 8                     return r;
 9                 r = p;
10             }
11         }

三、Function checkInvariants

 1 /**
 2          * 不變性檢查,保證紅黑樹的結構不改變。
 3          */
 4         static <K, V> boolean checkInvariants(HashMap.TreeNode<K, V> t) {
 5             HashMap.TreeNode<K, V> tp = t.parent, tl = t.left, tr = t.right,
 6                     tb = t.prev, tn = (HashMap.TreeNode<K, V>) t.next;
 7             if (tb != null && tb.next != t)
 8                 return false;
 9             if (tn != null && tn.prev != t)
10                 return false;
11             if (tp != null && t != tp.left && t != tp.right)
12                 return false;
13             if (tl != null && (tl.parent != t || tl.hash > t.hash))
14                 return false;
15             if (tr != null && (tr.parent != t || tr.hash < t.hash))
16                 return false;
17             if (t.red && tl != null && tl.red && tr != null && tr.red)
18                 return false;
19             if (tl != null && !checkInvariants(tl))
20                 return false;
21             if (tr != null && !checkInvariants(tr))
22                 return false;
23             return true;
24         }
25     }

 

四、Function moveRootToFront

 

 1 /**
 2          * 確保所給的root是第一個節點。也就是把所給的root移到第一個節點。確保桶的紅黑樹的跟節點是root
 3          */
 4         static <K, V> void moveRootToFront(HashMap.Node<K, V>[] tab, HashMap.TreeNode<K, V> root) {
 5             int n;
 6             if (root != null && tab != null && (n = tab.length) > 0) {
 7                 //根節點的位置
 8                 int index = (n - 1) & root.hash;
 9 
10                 //鏈表的操作,把root移到第一個節點。root的next指向原先的頭節點,原先的頭節點的prev指向root;
11                 //root的next的prev指向root的prev,root的prev指向root的next,即把root在prev和next中去掉
12                 HashMap.TreeNode<K, V> first = (HashMap.TreeNode<K, V>) tab[index];
13                 if (root != first) {
14                     HashMap.Node<K, V> rn;
15                     tab[index] = root;
16                     HashMap.TreeNode<K, V> rp = root.prev;
17                     if ((rn = root.next) != null)
18                         ((HashMap.TreeNode<K, V>) rn).prev = rp;
19                     if (rp != null)
20                         rp.next = rn;
21                     if (first != null)
22                         first.prev = root;
23                     root.next = first;
24                     root.prev = null;
25                 }
26                 //紅黑樹的一致性檢查
27                 assert checkInvariants(root);
28             }
29         }

 

五、Function find

 1 /**
 2          * Finds the node starting at root p with the given hash and key.
 3          * The kc argument caches comparableClassFor(key) upon first use
 4          * comparing keys.
 5          */
 6         final HashMap.TreeNode<K, V> find(int h, Object k, Class<?> kc) {
 7             HashMap.TreeNode<K, V> p = this;
 8             do {
 9                 int ph, dir;
10                 K pk;
11                 HashMap.TreeNode<K, V> pl = p.left, pr = p.right, q;
12 
13                 //p的hash > 目標hash, 則查找左子樹,否則右子樹
14                 if ((ph = p.hash) > h)
15                     p = pl;
16                 else if (ph < h)
17                     p = pr;
18                 //找到則返回
19                 else if ((pk = p.key) == k || (k != null && k.equals(pk)))
20                     return p;
21                 //如果左節點是Null則找右子樹,右節點是Null則找左子樹
22                 else if (pl == null)
23                     p = pr;
24                 else if (pr == null)
25                     p = pl;
26                 //如果不按照hash比較,則按照比較器比較,查找左子樹還是右子樹
27                 else if ((kc != null ||
28                         (kc = comparableClassFor(k)) != null) &&
29                         (dir = compareComparables(kc, k, pk)) != 0)
30                     p = (dir < 0) ? pl : pr;
31                 //如果在右子樹找到則直接返回
32                 else if ((q = pr.find(h, k, kc)) != null)
33                     return q;
34                 //否則在左子樹查找
35                 else
36                     p = pl;
37             } while (p != null);
38             //否則返回Null
39             return null;
40         }

 

六、Function tieBreakOrder

 1 /**
 2          * 在像紅黑樹插入即節點的時候,為了確定相同hashCode的節點插入的順序,
 3          * 設定了插入順序的規則,結果一定是不想等的。非左即右。
 4          */
 5         static int tieBreakOrder(Object a, Object b) {
 6             int d;
 7 
 8             //會對兩個類名相等的類進行比較
 9             if (a == null || b == null ||
10                     (d = a.getClass().getName().
11                             compareTo(b.getClass().getName())) == 0)
12                 //返回兩個類內存地址的hashCode比較結果,小的或者相都是-1,否則1,並非是類的hashCode的比較
13                 d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
14                         -1 : 1);
15             return d;
16         }

 

七、Function rotateLeft, 左旋

圖片來源(https://blog.csdn.net/sun_tttt/article/details/65445754)

 

 

 1 static <K, V> HashMap.TreeNode<K, V> rotateLeft(HashMap.TreeNode<K, V> root,
 2                                                         HashMap.TreeNode<K, V> p) {
 3             //三個節點,右節點,parent的parent節點,右的左節點
 4             HashMap.TreeNode<K, V> r, pp, rl;
 5             //該節點不為null並且右節點不為null
 6             if (p != null && (r = p.right) != null) {
 7                 //因為是左旋,所以如果右節點的左節點如果不為null,則rl的根節點設為p
 8                 if ((rl = p.right = r.left) != null)
 9                     rl.parent = p;
10                 //如果左旋后的頭節點為根節點,則根據紅黑樹的性質,顏色為黑色
11                 if ((pp = r.parent = p.parent) == null)
12                     (root = r).red = false;
13                 //因為是左旋,所以p的位置由pr取代,所以p的parent節點的p位置設為現在pr的位置。
14                 else if (pp.left == p)
15                     pp.left = r;
16                 else
17                     pp.right = r;
18                 //然后r的left是p,p的父節點是r
19                 r.left = p;
20                 p.parent = r;
21             }
22             return root;
23         }

 

八、Function rightRotate, 右旋 

圖片來源(https://blog.csdn.net/sun_tttt/article/details/65445754)

 

 1 static <K, V> HashMap.TreeNode<K, V> rotateRight(HashMap.TreeNode<K, V> root,
 2                                                          HashMap.TreeNode<K, V> p) {
 3             //定義3個節點,左節點,頭節點的頭節點,左節點的右節點
 4             HashMap.TreeNode<K, V> l, pp, lr;
 5             //要旋轉的節點和左節點不為空時
 6             if (p != null && (l = p.left) != null) {
 7                 //根據右旋,(原)頭節點的左節點為原先左節點的右節點,並且把其父節點設為原頭節點,即p
 8                 if ((lr = p.left = l.right) != null)
 9                     lr.parent = p;
10                 //同樣,如果現在的頭節點為根節點的話,標記節點的顏色為黑色
11                 if ((pp = l.parent = p.parent) == null)
12                     (root = l).red = false;
13                     //頭節點的頭節點設定其自節點
14                 else if (pp.right == p)
15                     pp.right = l;
16                 else
17                     pp.left = l;
18                 //同樣,根據右旋,指定現在的頭節點的右節點為原先的頭節點,原先的頭節點的父節點為現在的頭節點
19                 l.right = p;
20                 p.parent = l;
21             }
22             return root;
23         }

 

九、Function balanceInsertion   代碼和圖結合看很好理解過程,至於為什么會平衡還要分析。

圖片來源https://blog.csdn.net/weixin_42340670/article/details/80550932

1.無旋轉

2.旋轉情況1

3.旋轉情況2

 

 1 /*
 2         保證插入節點后,紅黑樹仍然是平衡的,代碼很長,結合圖看更好理解點,這樣看太抽象了。但是雖然邏輯復雜,但是足以
 3         見證紅黑樹的高效性,因為更新樹的話只是旋轉操作,即改變下指針的位置,並且設置一下節點的位置就可以了
 4          */
 5         static <K, V> HashMap.TreeNode<K, V> balanceInsertion(HashMap.TreeNode<K, V> root,
 6                                                               HashMap.TreeNode<K, V> x) {
 7             //先把節點設為紅色
 8             x.red = true;
 9             //定義四個節點
10             for (HashMap.TreeNode<K, V> xp, xpp, xppl, xppr; ; ) {
11                 //如果x是根節點,則把它設為黑色,並返回根節點
12                 if ((xp = x.parent) == null) {
13                     x.red = false;
14                     return x;
15                 }
16                 //如果x的父節點即xp是黑色,並且xp為根節點,則返回,什么也不做。
17                 else if (!xp.red || (xpp = xp.parent) == null)
18                     return root;
19                 //如果xp為xp父節點的左節點
20                 if (xp == (xppl = xpp.left)) {
21                     //如果xpp的右節點非空並且是紅色的,那么把其設為黑色,xpp的左節點也設為黑色,xpp設為紅色,並且x等於xpp
22                     if ((xppr = xpp.right) != null && xppr.red) {
23                         xppr.red = false;
24                         xp.red = false;
25                         xpp.red = true;
26                         x = xpp;
27                     }
28                     //如果xpp的右節點是空或者為黑色的話
29                     else {
30                         //如果x是xp的右節點,那么左旋xp節點,並且重新更新xp和xpp
31                         if (x == xp.right) {
32                             root = rotateLeft(root, x = xp);
33                             xpp = (xp = x.parent) == null ? null : xp.parent;
34                         }
35                         //如果x的父節點不為空,先把它設為黑色
36                         if (xp != null) {
37                             xp.red = false;
38                             //如果xp的父節點不為空,則先把xpp設為紅色,然后再右旋
39                             if (xpp != null) {
40                                 xpp.red = true;
41                                 root = rotateRight(root, xpp);
42                             }
43                         }
44                     }
45                 } 
46                 //如果xp為xp父節點的右右節點
47                 else {
48                     //如果xpp的左節點非空並且是紅色的話,把xppl設為黑色,xp設為黑色,xp的父節點設為紅色
49                     if (xppl != null && xppl.red) {
50                         xppl.red = false;
51                         xp.red = false;
52                         xpp.red = true;
53                         x = xpp;
54                     } 
55                     //如果xpp的左節點是空或者是黑色的話
56                     else {
57                         //如果x為父節點的左節點,則右旋xp節點,並重新設置xp,xpp
58                         if (x == xp.left) {
59                             root = rotateRight(root, x = xp);
60                             xpp = (xp = x.parent) == null ? null : xp.parent;
61                         }
62                         //如果x的父節點為空,
63                         if (xp != null) {
64                             //先把其設為黑色
65                             xp.red = false;
66                             //如果xp的父節點不為空,則xpp設為紅色,並左旋xpp節點
67                             if (xpp != null) {
68                                 xpp.red = true;
69                                 root = rotateLeft(root, xpp);
70                             }
71                         }
72                     }
73                 }
74             }
75         }

 

十、Function treeify

 1 /**
 2          * 把鏈表生成紅黑樹,返回頭節點
 3          */
 4         final void treeify(HashMap.Node<K, V>[] tab) {
 5             HashMap.TreeNode<K, V> root = null;
 6 
 7             //兩個指針,一個是鏈表的表頭,一個是下一個指針
 8             for (HashMap.TreeNode<K, V> x = this, next; x != null; x = next) {
 9                 next = (HashMap.TreeNode<K, V>) x.next;
10                 x.left = x.right = null;
11 
12                 //先設定 root為頭節點,parent為null,根節點為黑色,
13                 if (root == null) {
14                     x.parent = null;
15                     x.red = false;
16                     root = x;
17                 } else {
18                     K k = x.key;
19                     int h = x.hash;
20                     Class<?> kc = null;
21 
22                     //遍歷紅黑樹
23                     for (HashMap.TreeNode<K, V> p = root; ; ) {
24                         int dir, ph;
25                         K pk = p.key;
26                         //如果當前樹節點的hash > 鏈表節點的hash則dir值為-1
27                         if ((ph = p.hash) > h)
28                             dir = -1;
29                             //否則為1
30                         else if (ph < h)
31                             dir = 1;
32                             //如果不按照hash值比較的話,並且比較器不存在或者比較器比較的值是0的話,則把死結打開
33                         else if ((kc == null &&
34                                 (kc = comparableClassFor(k)) == null) ||
35                                 (dir = compareComparables(kc, k, pk)) == 0)
36                             dir = tieBreakOrder(k, pk);
37                         //設置一個紅黑樹的節點
38                         HashMap.TreeNode<K, V> xp = p;
39                         //設置節點的走向,如果dir <= 0則p為做節點,否則為右,也就是找到鏈表節點應該插入的位置
40                         if ((p = (dir <= 0) ? p.left : p.right) == null) {
41                             //設置鏈表節點的父節點
42                             x.parent = xp;
43                             if (dir <= 0)
44                                 xp.left = x;
45                             else
46                                 xp.right = x;
47                             //插入節點,並且不破壞紅黑樹的性質
48                             root = balanceInsertion(root, x);
49                             break;
50                         }
51                     }
52                 }
53             }
54             //設置頭節點
55             moveRootToFront(tab, root);
56         }

 

十一、Function split

 1  /**
 2      * 樹減枝,
 3      * 只有在resize的時候才調用該方法
 4      * @param map the map
 5      * @param tab 新的table
 6      * @param index 老的table的index
 7      * @param bit oldCap
 8      */
 9     final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
10         TreeNode<K,V> b = this;
11         // Relink into lo and hi lists, preserving order
12         TreeNode<K,V> loHead = null, loTail = null;
13         TreeNode<K,V> hiHead = null, hiTail = null;
14         // 如果size很小的話則把樹變成鏈表,用lc和hc來計數
15         int lc = 0, hc = 0;
16         // 遍歷紅黑樹
17         for (TreeNode<K,V> e = b, next; e != null; e = next) {
18             next = (TreeNode<K,V>)e.next;
19             e.next = null;
20             // 判斷將樹的節點歸為哪一部分
21             if ((e.hash & bit) == 0) {
22                 if ((e.prev = loTail) == null)
23                     loHead = e;
24                 else
25                     loTail.next = e;
26                 loTail = e;
27                 ++lc;
28             }
29             else {
30                 if ((e.prev = hiTail) == null)
31                     hiHead = e;
32                 else
33                     hiTail.next = e;
34                 hiTail = e;
35                 ++hc;
36             }
37         }
38 
39         //lo這部分樹放入的位置,index即原先的位置
40         if (loHead != null) {
41             if (lc <= UNTREEIFY_THRESHOLD)
42                 tab[index] = loHead.untreeify(map);
43             else {
44                 tab[index] = loHead;
45                 if (hiHead != null) // (else is already treeified)
46                     loHead.treeify(tab);
47             }
48         }
49         //index+bit這部分改變了
50         if (hiHead != null) {
51             if (hc <= UNTREEIFY_THRESHOLD)
52                 tab[index + bit] = hiHead.untreeify(map);
53             else {
54                 tab[index + bit] = hiHead;
55                 if (loHead != null)
56                     hiHead.treeify(tab);
57             }
58         }
59     }

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM