JDK源碼那些事兒之HashMap.TreeNode

前面幾篇文章已經講解過HashMap內部實現以及紅黑樹的基礎知識,今天這篇文章就講解以前HashMap中未講解的紅黑樹操做部分,若是沒了解紅黑樹,請去閱讀前面的兩篇文章,能更好的理解本章所講解的紅黑樹源碼操做,全文默認讀者已經瞭解紅黑樹的相關知識,接下來,就以HashMap.TreeNode來講明紅黑樹的源碼操做。java

前言

jdk版本:1.8

以HashMap.TreeNode爲例是由於以前HashMap的紅黑樹操做在文章省略了,這裏進行一個解釋,其實源碼裏並非只有這個地方用紅黑樹結構,可是整體上都大同小異,故只說明這一部分就好,觸類旁通的能力相信各位都應該擁有。node

類定義

static final class TreeNode<K,V> extends LinkedHashMap.Entry<K,V>

繼承LinkedHashMap.Entry,追溯源頭能夠到HashMap.Node,下面圖展現了對應的結構關係:數組

TreeNode繼承關係

屬性

/**
     * 父節點
     */
    TreeNode<K,V> parent;  // red-black tree links
    /**
     * 左子節點
     */
    TreeNode<K,V> left;
    /**
     * 右子節點
     */
    TreeNode<K,V> right;
    /**
     * 指向前一個節點
     */
    TreeNode<K,V> prev;    // needed to unlink next upon deletion
    /**
     * 是不是紅色節點
     */
    boolean red;

以上的屬性都是爲了知足紅黑樹特性而設置ide

構造方法

/**
     * 構造方法直接調用的父類方法
     * 最終仍是HashMap.Node的構造方法,調用代碼下面也列出來了
     */
    TreeNode(int hash, K key, V val, Node<K,V> next) {
        super(hash, key, val, next);
    }
    /**
     * HashMap.Node subclass for normal LinkedHashMap entries.
     */
    static class Entry<K,V> extends HashMap.Node<K,V> {
        Entry<K,V> before, after;
        Entry(int hash, K key, V value, Node<K,V> next) {
            super(hash, key, value, next);
        }
    }
    Node(int hash, K key, V value, Node<K,V> next) {
        this.hash = hash;
        this.key = key;
        this.value = value;
        this.next = next;
    }

Node和TreeNode相同的構造方法this

重要方法

root

實現上很是簡單,不斷向上遍歷,找到父節點爲空的節點即爲根節點spa

/**
     * 返回根節點TreeNode
     */
    final TreeNode<K,V> root() {
        for (TreeNode<K,V> r = this, p;;) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }

moveRootToFront

從註釋中也能看出當前方法的含義,確保根節點是bin桶(數組tab的其中一個元素)中的第一個節點,若是不是,則進行操做,將根節點放到tab數組上,這個跟HashMap結構有關,數組(鏈表+紅黑樹),在進行樹化,結構調整時,根節點可能會變化,原有數組tab對應索引指向的樹節點須要進行改變,指向新的根節點,這裏注意下,移動時不是修改紅黑樹是修改的鏈表結構,prev和next屬性code

/**
     * Ensures that the given root is the first node of its bin.
     */
    static <K,V> void moveRootToFront(Node<K,V>[] tab, TreeNode<K,V> root) {
        int n;
        if (root != null && tab != null && (n = tab.length) > 0) {
            // 找到當前樹所在的bin桶位置(即數組tab的位置)
            int index = (n - 1) & root.hash;
            // 將tab[index]的樹節點記錄下來爲first
            TreeNode<K,V> first = (TreeNode<K,V>)tab[index];
            // root沒有落在tab數組上,修改成root在tab數組上
            if (root != first) {
                Node<K,V> rn;
                // 這裏即替換掉tab[index]指向的原有節點,能夠理解成如今指向root節點
                tab[index] = root;
                // rp爲root指向的前一個節點
                TreeNode<K,V> rp = root.prev;
                // rn爲root的後一個節點
                // 將root先後節點關聯
                if ((rn = root.next) != null)
                    ((TreeNode<K,V>)rn).prev = rp;
                if (rp != null)
                    rp.next = rn;
                // first 和 root 節點進行關聯,first的前一個節點爲root
                if (first != null)
                    first.prev = root;
                // 修改root的鏈表屬性
                root.next = first;
                root.prev = null;
            }
            // 檢查紅黑樹一致性
            assert checkInvariants(root);
        }
    }

find

從當前節點開始使用給定的hash和key查找到對應的節點,只會判斷當前節點爲根節點的局部樹結構。這裏複習下整個HashMap查找過程,經過(length - 1) & hash 判斷bin桶位置(數組中的位置),這裏不是hash值相等,注意再判斷該位置處是什麼類型,鏈表仍是紅黑樹,鏈表類型,循環next遍歷,直到key值相等。紅黑樹類型 遞歸左右子樹遍歷,直到key值相等。orm

/**
         * Finds the node starting at root p with the given hash and key.
         * The kc argument caches comparableClassFor(key) upon first use
         * comparing keys.
         * kc : key class
         */
        final TreeNode<K,V> find(int h, Object k, Class<?> kc) {
            TreeNode<K,V> p = this;
            do {
                // ph:p的hash值
                int ph, dir; K pk;
                TreeNode<K,V> pl = p.left, pr = p.right, q;
                // 查找節點hash值小於p的hash值,搜索左子樹
                if ((ph = p.hash) > h)
                    p = pl;
                // 查找節點hash值大於p的hash值,搜索右子樹
                else if (ph < h)
                    p = pr;
                // key值相同,說明就是此p節點
                else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                    return p;
                // 若hash相等但key不等,向左右子樹非空的一側移動
                else if (pl == null)
                    p = pr;
                else if (pr == null)
                    p = pl;
                // 左右兩邊都不爲空
                // 判斷kc是不是k實現的可比較的類,是就比較k和pk的值,k<pk向左子樹移動不然向右子樹移動
                // hash相等,key不等,使用key實現的compareTo判斷大小
                else if ((kc != null ||
                          (kc = comparableClassFor(k)) != null) &&
                         (dir = compareComparables(kc, k, pk)) != 0)
                    p = (dir < 0) ? pl : pr;
                // 上面全部狀況依舊不能判斷左右,就先遞歸判斷右子樹,看是否匹配上,匹配上就賦右子樹,不然左子樹
                else if ((q = pr.find(h, k, kc)) != null)
                    return q;
                else
                    p = pl;
            } while (p != null);
            return null;
        }

tieBreakOrder

比較兩個對象的大小,返回值只能大於0或小於0,不能爲0,由於須要插入節點是放在左子樹仍是右子樹,這裏在兩個對象都不爲空時,先比較兩個對象的類名按字符串規則比較,若是類名比較不出來或者爲空則調用native方法去比較hashcode值,相等時返回-1對象

static int tieBreakOrder(Object a, Object b) {
        int d;
        if (a == null || b == null ||
            (d = a.getClass().getName().
             compareTo(b.getClass().getName())) == 0)
            d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                 -1 : 1);
        return d;
    }

tieBreakOrder

比較兩個對象的大小,返回值只能大於0或小於0,不能爲0,由於須要插入節點是放在左子樹仍是右子樹,這裏在兩個對象都不爲空時,先比較兩個對象的類名按字符串規則比較,若是類名比較不出來或者爲空則調用native方法去比較hashcode值,相等時返回-1繼承

static int tieBreakOrder(Object a, Object b) {
        int d;
        if (a == null || b == null ||
            (d = a.getClass().getName().
             compareTo(b.getClass().getName())) == 0)
            d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                 -1 : 1);
        return d;
    }

treeify

以當前TreeNode爲初始節點,循環處理鏈表上的每一個節點,每次插入樹節點都要進行平衡處理,保證紅黑樹的平衡。

/**
     * Forms tree of the nodes linked from this node.
     * @return root of tree
     */
    final void treeify(Node<K,V>[] tab) {
        TreeNode<K,V> root = null;
        // this當前TreeNode,通常應該以樹的根節點爲初始值,根據鏈表進行遍歷
        for (TreeNode<K,V> x = this, next; x != null; x = next) {
            next = (TreeNode<K,V>)x.next;
            x.left = x.right = null;
            // 首次root爲空 當前節點先設置爲root 節點顏色爲黑色
            if (root == null) {
                x.parent = null;
                x.red = false;
                root = x;
            }
            // root節點設置以後開始將鏈表節點依次插入處理,x=x.next插入root爲根節點的紅黑樹,循環處理
            else {
                K k = x.key;
                int h = x.hash;
                Class<?> kc = null;
                for (TreeNode<K,V> p = root;;) {
                    int dir, ph;
                    K pk = p.key;
                    // 比較hash值判斷處於左右子樹哪側
                    if ((ph = p.hash) > h)
                        // 節點處於p左子樹下
                        dir = -1;
                    else if (ph < h)
                        // 節點處於p右子樹下
                        dir = 1;
                    else if ((kc == null &&
                              (kc = comparableClassFor(k)) == null) ||
                             (dir = compareComparables(kc, k, pk)) == 0)
                        // hash值相等根據compareTo判斷,判斷不出來繼續執行tieBreakOrder判斷
                        dir = tieBreakOrder(k, pk);

                    // x的父節點設置爲xp
                    TreeNode<K,V> xp = p;
                    // 左右節點爲空,說明能夠將新增節點插入
                    // 非空,繼續循環子樹,p指向左子樹或右子樹,繼續循環判斷直到爲空的節點
                    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;
                    }
                }
            }
        }
        // root位置可能會在調整中變動,這裏需調用確保根節點落在tab數組上
        moveRootToFront(tab, root);
    }

untreeify

將樹轉換爲鏈表結構,將TreeNode轉化爲Node,改變next指向便可

/**
     * Returns a list of non-TreeNodes replacing those linked from
     * this node.
     */
    final Node<K,V> untreeify(HashMap<K,V> map) {
        // hd是鏈表首節點,tl是鏈表尾節點
        Node<K,V> hd = null, tl = null;
        for (Node<K,V> q = this; q != null; q = q.next) {
            Node<K,V> p = map.replacementNode(q, null);
            if (tl == null)
                hd = p;
            else
                tl.next = p;
            tl = p;
        }
        return hd;
    }
    Node<K,V> replacementNode(Node<K,V> p, Node<K,V> next) {
        return new Node<>(p.hash, p.key, p.value, next);
    }

putTreeVal

在紅黑樹結構中的putVal操做,先判斷節點應該存在的位置,循環判斷左右子樹,找到應該存在的位置。若該位置爲空,至關於本來無值,插入節點,而後進行平衡,桶位置調整。若該位置有值且相等,則直接返回,不須要插入操做。

/**
     * Tree version of putVal.
     */
    final TreeNode<K,V> putTreeVal(HashMap<K,V> map, Node<K,V>[] tab,
                                   int h, K k, V v) {
        Class<?> kc = null;
        boolean searched = false;
        // 獲取根節點
        TreeNode<K,V> root = (parent != null) ? root() : this;
        // 循環遍歷,相似find方法
        for (TreeNode<K,V> p = root;;) {
            int dir, ph; K pk;
            if ((ph = p.hash) > h)
                dir = -1;
            else if (ph < h)
                dir = 1;
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                return p;
            else if ((kc == null &&
                      (kc = comparableClassFor(k)) == null) ||
                     (dir = compareComparables(kc, k, pk)) == 0) {
                if (!searched) {
                    // 遞歸左右子樹進行查找是否已經存在
                    // 只需判斷一次便可,第二次再也不執行
                    TreeNode<K,V> q, ch;
                    searched = true;
                    if (((ch = p.left) != null &&
                         (q = ch.find(h, k, kc)) != null) ||
                        ((ch = p.right) != null &&
                         (q = ch.find(h, k, kc)) != null))
                        return q;
                }
                // 上面都比較不出來,經過這個方法比較出來
                dir = tieBreakOrder(k, pk);
            }

            // 判斷當前插入位置是否爲空,爲空才插入,非空則繼續判斷,根據dir判斷是左仍是右子樹
            TreeNode<K,V> xp = p;
            if ((p = (dir <= 0) ? p.left : p.right) == null) {

                Node<K,V> xpn = xp.next;
                TreeNode<K,V> x = map.newTreeNode(h, k, v, xpn);
                if (dir <= 0)
                    xp.left = x;
                else
                    xp.right = x;
                xp.next = x;
                x.parent = x.prev = xp;
                if (xpn != null)
                    ((TreeNode<K,V>)xpn).prev = x;
                // balanceInsertion 插入調整平衡
                // moveRootToFront 確保root節點落在tab數組上爲首節點
                moveRootToFront(tab, balanceInsertion(root, x));
                return null;
            }
        }
    }

removeTreeNode

刪除樹節點操做,movable:判斷是否須要調整root節點(放置在tab上),在HashMap裏removeNode方法中使用,對應的刪除節點進行調用,具體自行查看源碼部分。其實,想下,刪除操做至關於將鏈表節點之間的關聯從新梳理,修正兩部分,第一部分是鏈表的關係修正,第二部分是樹的關係修正,而後自平衡操做便可。

final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,boolean movable) {
        int n;
        // 判斷非空
        if (tab == null || (n = tab.length) == 0)
            return;
        // 計算當前樹節點所在的桶的位置(tab索引位置)
        int index = (n - 1) & hash;
        TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
        // succ指向當前刪除節點的後一個節點,pred指向當前刪除節點的前一個節點
        TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
        if (pred == null)
            // 前一個節點爲空,說明刪除的節點爲根節點,first和tab[index]需指向刪除節點的後一個節點
            tab[index] = first = succ;
        else
            // 前一個節點不爲空,則前一個節點的後一個節點爲succ(刪除以後的關聯)
            pred.next = succ;
        if (succ != null)
            // 後一個節點不爲空,則後一個節點的前一個節點爲pred,構建關聯
            succ.prev = pred;
        if (first == null)
            // first爲空則代表當前桶內無節點,直接return
            return;
        if (root.parent != null)
            // 目前的root節點有父類,須要調整
            root = root.root();
        if (root == null || root.right == null ||
            (rl = root.left) == null || rl.left == null) {
            // 根自身或者左右兒子其中一個爲空或左子樹的子樹爲空需轉換爲鏈表結構
            // 考慮到紅黑樹特性,這幾種狀況下,節點較少,進行樹向鏈表的轉化
            tab[index] = first.untreeify(map);  // too small
            return;
        }
        // p指向刪除的節點
        // 上面修正鏈表的關係,下面修正樹中的關係
        TreeNode<K,V> p = this, pl = left, pr = right, replacement;
        // 刪除節點的左右子樹都不爲空,參考紅黑樹刪除4種狀況
        if (pl != null && pr != null) {
            TreeNode<K,V> s = pr, sl;
            while ((sl = s.left) != null) // find successor
                // 尋找右子樹中最左的葉結點做爲後繼節點,s指向後繼節點
                s = sl;
            // 交換後繼節點和刪除節點的顏色,從這裏開始在後繼節點上進行刪除處理
            boolean c = s.red; s.red = p.red; p.red = c; // swap colors
            TreeNode<K,V> sr = s.right;
            TreeNode<K,V> pp = p.parent;
            if (s == pr) { // p was s's direct parent
                // s是p的右兒子,直接父子關係,交換p和s的位置
                // 改變兩節點的指向,至關於交換值
                p.parent = s;
                s.right = p;
            }
            else {
                TreeNode<K,V> sp = s.parent;
                // 刪除節點的父節點指向其後繼節點的父節點
                if ((p.parent = sp) != null) {
                    // 判斷s是左子節點仍是右子節點,s父節點一側指向刪除節點p
                    if (s == sp.left)
                        sp.left = p;
                    else
                        sp.right = p;
                }
                if ((s.right = pr) != null)
                    // s右節點指向本來p的右節點,不爲空,原p右子節點的父節點指向s
                    pr.parent = s;
            }
            // 修改p的左節點爲空,由於如今p已經在後繼節點上
            p.left = null;
            // 兩個if是調整p和s交換後節點的指向關係
            if ((p.right = sr) != null)
                sr.parent = p;
            if ((s.left = pl) != null)
                pl.parent = s;
            if ((s.parent = pp) == null)
                // p的父親成爲s的父親,爲空說明是根節點,root指向s
                root = s;
            else if (p == pp.left)
                // p的父親的左兒子原爲p,現爲s
                pp.left = s;
            else
                // 不然右兒子爲s
                pp.right = s;
            // 若s結點有右兒子(s沒有左兒子,由於s是後繼節點),則replacement爲這個右兒子不然爲p
            // replacement至關於p和後繼節點s交換後,刪除s位置取代其位置的節點,參考紅黑樹刪除過程理解
            if (sr != null)
                replacement = sr;
            else
                replacement = p;
        }
        // 刪除節點的左右子樹一側爲空,參考紅黑樹刪除4種狀況
        // 若p的左右兒子爲空,則replacement爲非空的一方,不然爲p本身
        else if (pl != null)
            replacement = pl;
        else if (pr != null)
            replacement = pr;
        else
            replacement = p;
        // replacement不爲p,即不爲葉子節點(至關於以前所講的紅黑樹中有兩個NIL節點的節點)
        // 處理鏈接關係
        if (replacement != p) {
            TreeNode<K,V> pp = replacement.parent = p.parent;
            if (pp == null)
                root = replacement;
            else if (p == pp.left)
                pp.left = replacement;
            else
                pp.right = replacement;
            // 移除p節點
            p.left = p.right = p.parent = null;
        }

        // 紅黑樹自平衡調節,若是爲紅色,則不須要進行調整,不然須要自平衡調整,後面會對這個方法進行分析
        TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);

        // 後繼節點無子節點,直接移除p節點即能保持平衡
        if (replacement == p) {  // detach
            TreeNode<K,V> pp = p.parent;
            p.parent = null;
            if (pp != null) {
                if (p == pp.left)
                    pp.left = null;
                else if (p == pp.right)
                    pp.right = null;
            }
        }
        if (movable)
            // 調整root使其落在tab數組上
            moveRootToFront(tab, r);
    }

split

拆分,在HashMap.resize方法中調用 ((TreeNode<K,V>)e).split(this, newTab, j, oldCap)。在擴容以後,紅黑樹和鏈表由於擴容的緣由致使本來在一個數組元素下的Node節點分爲高低位兩部分(參考HashMap.resize鏈表部分的改變,是相同的),請查看HashMap的文章自行回顧,低位樹即當前位置,高位樹則在新擴容的tab上

final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
        TreeNode<K,V> b = this;
        // Relink into lo and hi lists, preserving order
        // 分紅高位樹和低位樹,頭尾節點先聲明
        TreeNode<K,V> loHead = null, loTail = null;
        TreeNode<K,V> hiHead = null, hiTail = null;
        int lc = 0, hc = 0;
        // 循環處理節點
        for (TreeNode<K,V> e = b, next; e != null; e = next) {
            next = (TreeNode<K,V>)e.next;
            e.next = null;
            // 這裏bit表明擴容的二進制位(數值是擴容前的容量大小),不明白的也請參考以前的HashMap講解文章
            if ((e.hash & bit) == 0) {
                // 0說明在低位樹,即原位置
                if ((e.prev = loTail) == null)
                    // 首節點設置
                    loHead = e;
                else
                    // 鏈表next設置
                    loTail.next = e;
                loTail = e;
                ++lc;
            }
            else {
                // 同上,主要是在高位樹上處理
                if ((e.prev = hiTail) == null)
                    hiHead = e;
                else
                    hiTail.next = e;
                hiTail = e;
                ++hc;
            }
        }

        // 對高低位樹進行處理,將數組節點指向樹根節點或者鏈表首節點
        if (loHead != null) {
            if (lc <= UNTREEIFY_THRESHOLD)
                // 拆分以後節點小於非樹化閾值,轉成鏈表結構
                tab[index] = loHead.untreeify(map);
            else {
                tab[index] = loHead;
                // 高位樹爲空則表示節點所有留在了低位樹,不須要進行樹化操做,已經樹化過了
                if (hiHead != null) // (else is already treeified)
                    loHead.treeify(tab);
            }
        }
        if (hiHead != null) {
            if (hc <= UNTREEIFY_THRESHOLD)
                tab[index + bit] = hiHead.untreeify(map);
            else {
                // 高位所處的位置爲本來位置+舊數組的大小即bit
                tab[index + bit] = hiHead;
                if (loHead != null)
                    hiHead.treeify(tab);
            }
        }
    }

rotateLeft

左旋操做,參考紅黑樹講解中的圖來理解,本身動手畫一畫也能明白,右旋操做相似,再也不多說

左旋

static <K,V> TreeNode<K,V> rotateLeft(TreeNode<K,V> root,
                                          TreeNode<K,V> p) {
        TreeNode<K,V> r, pp, rl;
        // r表明M的右子節點N,先決條件,p(M),r(N)不能爲空
        if (p != null && (r = p.right) != null) {
            // r的左子節點成爲p的右子節點,即B成爲M的右子節點
            if ((rl = p.right = r.left) != null)
                // r的左子節點父節點指向p,即修改B父節點指向M
                rl.parent = p;
            // p的父節點成爲r的父節點,即N父節點爲原M的父節點
            if ((pp = r.parent = p.parent) == null)
                // r的父節點爲空,則r爲root節點,設置爲黑色,即N父節點爲空,N設置爲根節點
                (root = r).red = false;
            // 原p節點是左子樹,即M是左子樹
            else if (pp.left == p)
                // 現調整後修改N父節點左子指向N
                pp.left = r;
            else
                // r的父節點的右子節點需重設,即調整後修改N父節點右子指向N
                pp.right = r;
            // p和r的位置關係修改,即M與N的關係重設
            r.left = p;
            p.parent = r;
        }
        return root;
    }

balanceInsertion

插入節點以後進行平衡調整,x爲新添加的節點,root爲樹的根節點,返回根節點。

static <K,V> TreeNode<K,V> balanceInsertion(TreeNode<K,V> root,
                                            TreeNode<K,V> x) {
        // 根據紅黑樹屬性插入的節點默認設置爲紅色
        x.red = true;
        // 經過循環進行調整,從葉子到根節點進行局部調整
        for (TreeNode<K,V> xp, xpp, xppl, xppr;;) {
            // x的父節點爲空,說明x節點爲根節點,將顏色置爲黑便可
            // 符合插入節點第一種狀況
            if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            }
            // x父節點爲黑色或者x的祖父節點爲空
            // 符合插入節點第二種狀況
            else if (!xp.red || (xpp = xp.parent) == null)
                return root;
            // 下面狀況中x的顏色都爲紅色,由於上邊已經判斷過黑色
            // x的父節點爲x祖父節點的左兒子,插入狀況下三四種狀況須要區分左仍是右
            if (xp == (xppl = xpp.left)) {
                // x祖父右兒子,即x的叔叔不爲空,且爲紅色
                // 符合插入節點第三種狀況
                if ((xppr = xpp.right) != null && xppr.red) {
                    // x的叔叔修改成黑色
                    xppr.red = false;
                    // x的父節點修改成黑色
                    xp.red = false;
                    // x的祖父節點修改成紅色
                    xpp.red = true;
                    // x指向x的祖父節點,以祖父節點循環繼續向上調整,至關於xpp是插入節點
                    x = xpp;
                }
                else {
                    // x的叔叔是黑色且x是右兒子,至關於在樹的「內部」
                    // 符合插入節點第四種狀況的第一種狀態
                    if (x == xp.right) {
                        // 以x父節點進行左旋
                        root = rotateLeft(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    // 這裏處理第二種狀態,至關於在樹的「外部」
                    if (xp != null) {
                        // x的父節點改成黑色,x的祖父節點改成紅色後對x的祖父節點進行右旋轉
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateRight(root, xpp);
                        }
                    }
                }
            }
            // x的父節點爲x祖父節點的右兒子
            // 下面跟上邊相似,對稱關係
            else {
                if (xppl != null && xppl.red) {
                    xppl.red = false;
                    xp.red = false;
                    xpp.red = true;
                    x = xpp;
                }
                else {
                    if (x == xp.left) {
                        root = rotateRight(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateLeft(root, xpp);
                        }
                    }
                }
            }
        }
    }

balanceDeletion

刪除節點後自平衡操做,x是刪除節點的替換節點,注意下

static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,TreeNode<K,V> x) {
        // 循環操做,平衡局部以後繼續判斷調整
        for (TreeNode<K,V> xp, xpl, xpr;;)  {
            // 刪除節點爲空或x爲根節點直接返回,平衡調整完畢x=root
            if (x == null || x == root)
                return root;
            // 刪除後x父節點爲空,說明x爲根節點,x置爲黑色,紅黑樹平衡
            // 參考紅黑樹刪除文章中的3種狀況中的第一種狀況,整棵樹的根節點須要將根節點置黑
            else if ((xp = x.parent) == null) {
                // xp爲空 x爲根節點,x置爲黑色
                x.red = false;
                return x;
            }
            // x爲紅色,原節點必爲黑色,變色操做便可知足紅黑樹特性達到平衡
            // 參考紅黑樹刪除文章中的3種狀況中的第二種狀況
            else if (x.red) {
                x.red = false;
                return root;
            }
            // 區分x爲左子節點仍是右子節點
            else if ((xpl = xp.left) == x) {
                // x的叔叔爲紅色
                // 符合3種狀況中的第三種狀況中的第二種狀況
                if ((xpr = xp.right) != null && xpr.red) {
                    // 先進行變色,而後進行左旋操做,xpr指向xp新的右子
                    xpr.red = false;
                    xp.red = true;
                    root = rotateLeft(root, xp);
                    xpr = (xp = x.parent) == null ? null : xp.right;
                }
                // x的兄弟爲空
                if (xpr == null)
                    // x指向x的父節點,繼續以x父節點調整平衡
                    x = xp;
                else {
                    TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                    // 符合3種狀況中的第三種狀況中的第三種狀況
                    if ((sr == null || !sr.red) &&
                        (sl == null || !sl.red)) {
                        // sr,sl兩個兄弟都是黑色,x的兄弟設置爲紅色,x指向x的父節點繼續向上循環調整平衡
                        xpr.red = true;
                        x = xp;
                    }
                    else {
                        // 符合3種狀況中的第三種狀況中的第五種狀況
                        if (sr == null || !sr.red) {
                            // sr爲空或者爲黑色
                            if (sl != null)
                                // sl非空說明爲紅色,設置爲黑色
                                sl.red = false;
                            // x的兄弟設置爲紅色
                            xpr.red = true;
                            // 右旋
                            root = rotateRight(root, xpr);
                            xpr = (xp = x.parent) == null ?
                                null : xp.right;
                        }
                        // 符合3種狀況中的第三種狀況中的第六種狀況
                        if (xpr != null) {
                            // xpr顏色設置
                            xpr.red = (xp == null) ? false : xp.red;
                            if ((sr = xpr.right) != null)
                                // xpr 右孩子設置爲黑色
                                sr.red = false;
                        }
                        if (xp != null) {
                            xp.red = false;
                            // 左旋操做
                            root = rotateLeft(root, xp);
                        }
                        x = root;
                    }
                }
            }
            // 和上邊是對稱操做
            else { // symmetric
                if (xpl != null && xpl.red) {
                    xpl.red = false;
                    xp.red = true;
                    root = rotateRight(root, xp);
                    xpl = (xp = x.parent) == null ? null : xp.left;
                }
                if (xpl == null)
                    x = xp;
                else {
                    TreeNode<K,V> sl = xpl.left, sr = xpl.right;
                    if ((sl == null || !sl.red) &&
                        (sr == null || !sr.red)) {
                        xpl.red = true;
                        x = xp;
                    }
                    else {
                        if (sl == null || !sl.red) {
                            if (sr != null)
                                sr.red = false;
                            xpl.red = true;
                            root = rotateLeft(root, xpl);
                            xpl = (xp = x.parent) == null ?
                                null : xp.left;
                        }
                        if (xpl != null) {
                            xpl.red = (xp == null) ? false : xp.red;
                            if ((sl = xpl.left) != null)
                                sl.red = false;
                        }
                        if (xp != null) {
                            xp.red = false;
                            root = rotateRight(root, xp);
                        }
                        x = root;
                    }
                }
            }
        }
    }

checkInvariants

對整棵樹進行紅黑樹一致性的檢查 目前僅在檢查root是否落在table上時調用,知足紅黑樹的特性以及節點指向的正確性

static <K,V> boolean checkInvariants(TreeNode<K,V> t) {
        TreeNode<K,V> tp = t.parent, tl = t.left, tr = t.right,
            tb = t.prev, tn = (TreeNode<K,V>)t.next;
        // t的前一個節點的後一個節點應爲t
        if (tb != null && tb.next != t)
            return false;
        // tn的後一個節點的前一個節點應爲t
        if (tn != null && tn.prev != t)
            return false;
        // t的父節點的左子節點或右子節點應爲t
        if (tp != null && t != tp.left && t != tp.right)
            return false;
        // t的左子節點的父節點應爲t而且 t的左子節點hash值小於t的hash值
        if (tl != null && (tl.parent != t || tl.hash > t.hash))
            return false;
        // t的右子節點的父節點應爲t而且 t的右子節點hash值大於t的hash值
        if (tr != null && (tr.parent != t || tr.hash < t.hash))
            return false;
        // t和t的子節點不能同時是紅色,紅黑樹特性
        if (t.red && tl != null && tl.red && tr != null && tr.red)
            return false;
        // 左子節點遞歸檢查
        if (tl != null && !checkInvariants(tl))
            return false;
        // 右子節點遞歸檢查
        if (tr != null && !checkInvariants(tr))
            return false;
        return true;
    }

總結

至此,關於TreeNode的代碼講解部分已經完成,相似的源碼TreeMap等使用紅黑樹結構的類基本操做都是相似源碼,能夠自行查看,重要的部分在於插入和刪除是如何作到的,在以後如何進行自平衡操做的,但願對各位讀者有所幫助

相關文章
相關標籤/搜索