文章出處

//對treeMap的紅黑樹理解注解. 2017.02.16 by 何錦彬  JDK,1.7.51

/** From CLR */ private void fixAfterInsertion(Entry<K, V> x) { //新加入紅黑樹的默認節點就是紅色 x.color = RED; /** * 1. 如為根節點直接跳出 */ while (x != null && x != root && x.parent.color == RED) { if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { //如果X的父節點(P)是其父節點的父節點(G)的左節點 //即 下面這種情況 /** * G * P(RED) U */ //獲取其叔(U)節點 Entry<K, V> y = rightOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { // 這種情況 /** * G * P(RED) U(RED) * X */ //如果叔節點是紅色的(父節點有判斷是紅色). 即是雙紅色,比較好辦,通過改變顏色就行. 把P和U都設置成黑色然后,X加到P節點。 G節點當作新加入節點繼續迭代 setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); x = parentOf(parentOf(x)); } else { //處理紅父,黑叔的情況 if (x == rightOf(parentOf(x))) { // 這種情況 /** * G * P(RED) U(BLACK) * X */ //如果X是右邊節點 x = parentOf(x); // 進行左旋 rotateLeft(x); } //左旋后,是這種情況了 /** * G * P(RED) U(BLACK) * X */ // 到這,X只能是左節點了,而且P是紅色,U是黑色的情況 //把P和G改成黑色,以G為節點進行右旋 setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); rotateRight(parentOf(parentOf(x))); } } else { //父節點在右邊的 /** * G * U P(RED) */ //獲取U Entry<K, V> y = leftOf(parentOf(parentOf(x))); if (colorOf(y) == RED) { //紅父紅叔的情況 /** * G * U(RED) P(RED) */ setColor(parentOf(x), BLACK); setColor(y, BLACK); setColor(parentOf(parentOf(x)), RED); //把G當作新插入的節點繼續進行迭代 x = parentOf(parentOf(x)); } else { //紅父黑叔,并且是右父的情況 /** * G * U(RED) P(RED) */ if (x == leftOf(parentOf(x))) { x = parentOf(x); //以P為節點進行右旋 rotateRight(x); } //右旋后 /** * G * U(BLACK) P(RED) * X */ setColor(parentOf(x), BLACK); setColor(parentOf(parentOf(x)), RED); //以G為節點進行左旋 rotateLeft(parentOf(parentOf(x))); } } } //紅黑樹的根節點始終是黑色 root.color = BLACK; }

 

 

2, HASHMAP的死鏈問題

  

//對HashMap死鏈理解的注解 . 2017.02.17 by 何錦彬 JDK,1.7.51

void transfer(Entry[] newTable, boolean rehash) { //獲取新table的容量 int newCapacity = newTable.length; //迭代以前的數組 for (Entry<K,V> e : table) { //如果數組上有元素 while(null != e) { // 賦值next Entry<K,V> next = e.next; //獲取e在新的table里的位置 if (rehash) { e.hash = null == e.key ? 0 : hash(e.key); } int i = indexFor(e.hash, newCapacity); //把e指向當前的新數組里的第一個元素,這里會并發了,如果在這斷點等待下個線程過來,就會死循環,嘗試下 e.next = newTable[i]; //替代新數組的位置 newTable[i] = e; e = next; } } }

  

 擴容前


[ 1 ] [ 2 ] [ 3 ] [ 空]
  5     10

第一個線程擴容后,數組鏈表如下

[ 1 ] [ 10 ] [3] [] [] [] []
          2

第二個線程又把從頭把2指向10,然后2和10形成了個死循環

 

 

HashMap在 JDK8后 把數組鏈表變成了數組+鏈表+紅黑樹. 鏈表為O(n),而紅黑樹為O(logN)

 for (int binCount = 0; ; ++binCount) {
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //JDK8 的hashmap,鏈表到了8就需要變成顆紅黑樹了
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            treeifyBin(tab, hash);
                        break;
                    }

 

調整紅黑樹的方法其實和treeMap的一樣了

 

如下:

//hashmap的紅黑樹平衡
         static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                                            TreeNode<K,V> x) {
                    x.red = true;
                    //死循環加變量定義,總感覺JAVA很少這樣寫代碼 哈
                    for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
                        //xp X父節點, XPP X的祖父節點,  XPPL 祖父左節點  XXPR 祖父右節點  
                        if ((xp = x.parent) == null) {
                            x.red = false;
                            return x;
                        }
                        // 如果父節點是黑色, 或者XP父節點是空,直接返回
                        else if (!xp.red || (xpp = xp.parent) == null)
                            return root;
                        
                        // 下面的代碼就和上面的很treeMap像了,
                       
                        if (xp == (xppl = xpp.left)) {
                             // 第一種情況, 賦值xppl
                            //父節點是左節點的情況,下面這種
                            /**
                             *                         G
                             *               P(RED)              U
                             */              
                            if ((xppr = xpp.right) != null && xppr.red) {
                                //如果紅叔的情況
                                 // 這種情況
                                /**
                                 *                         G
                                 *               P(RED)              U(RED)
                                 *          X
                                 */
                                 //改變其顏色,
                                xppr.red = false;
                                xp.red = false;
                                xpp.red = true;
                                x = xpp;
                            }
                            else {
                                 // 黑叔的情況
                                //  這種情況
                              /**
                                 *                         G
                                 *               P(RED)              U(BLACK)
                                 */
                                if (x == xp.right) {
                                    //如果插入節點在右邊 這種
                                     // 這種情況
                                    /**
                                     *                           G
                                     *            P(RED)                        U(BLACK)
                                     *                     X
                                     */
                                    //需要進行左旋
                                    root = rotateLeft(root, x = xp);
                                    xpp = (xp = x.parent) == null ? null : xp.parent;
                                }
                                //左旋后情況都是這種了
                                /**
                                 *                           G
                                 *            P(RED)                        U(BLACK)
                                 *      X
                                 */
                                // 到這,X只能是左節點了,而且P是紅色,U是黑色的情況
                                if (xp != null) {
                                //把P和G改成黑色,以G為節點進行右旋
                                    xp.red = false;
                                    if (xpp != null) {
                                        xpp.red = true;
                                        root = rotateRight(root, xpp);
                                    }
                                }
                            }
                        }
                        else {
                             //父節點在右邊的
                            /**
                             *                         G
                             *                 U               P(RED)
                             */              
                            //獲取U
                            if (xppl != null && xppl.red) {
                                //紅父紅叔的情況
                                 /**
                                 *                           G
                                 *                 U(RED)               P(RED)
                                 */              
                                xppl.red = false;
                                xp.red = false;
                                xpp.red = true;
                                x = xpp;
                            }
                            else {
                             
                                if (x == xp.left) {
                                    //如果插入的X是右節點
                                  /**
                                     *                            G
                                     *            U(BLACK)                        P(RED)
                                     *                                       X              
                                     */
                                    root = rotateRight(root, x = xp);
                                    xpp = (xp = x.parent) == null ? null : xp.parent;
                                }
                                //右旋后
                                /**
                                 *                            G
                                 *            U(BLACK)                        P(RED)
                                 *                                                        X
                                 */
                                if (xp != null) {
                                    xp.red = false;
                                    if (xpp != null) {
                                        xpp.red = true;
                                        root = rotateLeft(root, xpp);
                                    }
                                }
                            }
                        }
                    }

 


文章列表


不含病毒。www.avast.com
arrow
arrow
    全站熱搜
    創作者介紹
    創作者 大師兄 的頭像
    大師兄

    IT工程師數位筆記本

    大師兄 發表在 痞客邦 留言(0) 人氣()