TreeMap和HashMap中红黑树源码解读

TreeMap插入

public V put(K key, V value) {
    Entry<K,V> t = root;
    // root为null,新建节点设置为root
    if (t == null) {
        compare(key, key); // type (and possibly null) check

        root = new Entry<>(key, value, null);
        size = 1;
        modCount++;
        return null;
    }
    int cmp;
    Entry<K,V> parent;
    // split comparator and comparable paths
    Comparator<? super K> cpr = comparator;
    if (cpr != null) {
        do {
            parent = t;
            cmp = cpr.compare(key, t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                // 找到节点替换新值返回旧值,未找到节点退出循环
                return t.setValue(value);
        } while (t != null);
    }
    else {
        if (key == null)
            throw new NullPointerException();
        @SuppressWarnings("unchecked")
            Comparable<? super K> k = (Comparable<? super K>) key;
        do {
            parent = t;
            cmp = k.compareTo(t.key);
            if (cmp < 0)
                t = t.left;
            else if (cmp > 0)
                t = t.right;
            else
                return t.setValue(value);
        } while (t != null);
    }
    // 插入节点
    Entry<K,V> e = new Entry<>(key, value, parent);
    if (cmp < 0)
        parent.left = e;
    else
        parent.right = e;
    // 插入修正
    fixAfterInsertion(e);
    size++;
    modCount++;
    return null;
}
private void fixAfterInsertion(Entry<K,V> x) {
    // 因创建的新节点默认是黑色,这里要改成红色
    x.color = RED;
    // 节点为null或root或父节点为黑色时,不再循环(此处x其实不会为null)
    while (x != null && x != root && x.parent.color == RED) {
        // 循环内,父节点一定是红色,祖父节点一定是黑色
        // 当前节点的父节点是左节点时
        if (parentOf(x) == leftOf(parentOf(parentOf(x)))) {
            // y指向叔叔节点
            Entry<K,V> y = rightOf(parentOf(parentOf(x)));
            // 叔叔节点为红色
            if (colorOf(y) == RED) {
                // 祖父节点与父节点和叔叔节点交换颜色
                setColor(parentOf(x), BLACK);
                setColor(y, BLACK);
                setColor(parentOf(parentOf(x)), RED);
                // 祖父节点变红色后,需以祖父节点为当前节点继续判断是否有两个红色节点相连的情况
                x = parentOf(parentOf(x));
            // 叔叔节点为黑色(包含为null叶子节点的情况)
            } else {
                // 当前节点是父节点的右子节点
                if (x == rightOf(parentOf(x))) {
                    // 以父节点为支点左旋
                    x = parentOf(x);
                    rotateLeft(x);
                }
                // 父节点置为黑色,祖父节点置为红色,并右旋
                setColor(parentOf(x), BLACK);
                setColor(parentOf(parentOf(x)), RED);
                rotateRight(parentOf(parentOf(x)));
                // 变色加右旋后,已满足条件,不需继续循环
            }
        // 当前节点的父节点是右节点时,镜像处理即可
        } else {
            ...
        }
    }
    // 最后将root设置为黑色
    root.color = BLACK;
}

TreeMap删除

public V remove(Object key) {
    // 先查找
    Entry<K,V> p = getEntry(key);
    if (p == null)
        return null;

    V oldValue = p.value;
    // 找到了,再删除
    deleteEntry(p);
    return oldValue;
}
private void deleteEntry(Entry<K,V> p) {
    modCount++;
    size--;

    // If strictly internal, copy successor's element to p and then make p
    // point to successor.
    // 有两个子节点,则需转化
    if (p.left != null && p.right != null) {
        // 找后继节点
        Entry<K,V> s = successor(p);
        // 将后继节点的key和value赋值给当前节点
        p.key = s.key;
        p.value = s.value;
        // p指向后继节点,即为交换当前节点和后继节点,转化为只有一个子节点和没有子节点的情况
        p = s;
    } // p has 2 children

    // Start fixup at replacement node, if it exists.
    // 只有一个子节点replacement指向子节点,没有子节点replacement为null
    Entry<K,V> replacement = (p.left != null ? p.left : p.right);
    // replacement不为null,即为有一个子节点,此时只有一种情况,p为黑色,replacement为红色
    if (replacement != null) {
        // Link replacement to parent
        // replacement连接父节点
        replacement.parent = p.parent;
        if (p.parent == null)
            root = replacement;
        else if (p == p.parent.left)
            p.parent.left  = replacement;
        else
            p.parent.right = replacement;

        // Null out links so they are OK to use by fixAfterDeletion.
        // 清除当前节点的连接
        p.left = p.right = p.parent = null;

        // Fix replacement
        // 此处其实p一定是黑色,只需将replacement置为黑色
        if (p.color == BLACK)
            fixAfterDeletion(replacement);
    } else if (p.parent == null) { // return if we are the only node.
        // p原为root节点
        root = null;
    } else { //  No children. Use self as phantom replacement and unlink.
        // 没有子节点时,当前节点是黑色,需要调整
        if (p.color == BLACK)
            fixAfterDeletion(p);
        // 最后删除p的连接
        if (p.parent != null) {
            if (p == p.parent.left)
                p.parent.left = null;
            else if (p == p.parent.right)
                p.parent.right = null;
            p.parent = null;
        }
    }
}
private void fixAfterDeletion(Entry<K,V> x) {
    // 节点为黑色,非root时,才要循环
    while (x != root && colorOf(x) == BLACK) {
        // x是左节点
        if (x == leftOf(parentOf(x))) {
            // sib指向兄弟节点
            Entry<K,V> sib = rightOf(parentOf(x));
            // 兄弟节点是红色时
            if (colorOf(sib) == RED) {
                setColor(sib, BLACK);
                setColor(parentOf(x), RED);
                // 左旋,转化成兄弟节点为黑色的情况
                rotateLeft(parentOf(x));
                // sib指向新的兄弟节点,继续按其他情况处理
                sib = rightOf(parentOf(x));
            }
            // 兄弟节点的两个子节点都是黑色时,包括子节点为null的情况,隐含信息:兄弟节点也为黑色
            if (colorOf(leftOf(sib))  == BLACK &&
                colorOf(rightOf(sib)) == BLACK) {
                // 相当于将节点和兄弟节点都变红色,x指向父节点,当父节点是红色时,退出循环并变为黑色,父节点是黑色时,则再循环处理
                setColor(sib, RED);
                x = parentOf(x);
            } else {
                // 如果右侄子是黑色的,隐含信息:左侄子是红色的,两个都是黑色的情况进入上面的分支了
                if (colorOf(rightOf(sib)) == BLACK) {
                    // 右旋,让右侄子变红色,变成另一种情况来继续处理
                    setColor(leftOf(sib), BLACK);
                    setColor(sib, RED);
                    rotateRight(sib);
                    sib = rightOf(parentOf(x));
                }
                // 左旋,将右侄子的红色转移给当前节点
                setColor(sib, colorOf(parentOf(x)));
                setColor(parentOf(x), BLACK);
                setColor(rightOf(sib), BLACK);
                rotateLeft(parentOf(x));
                // 将x指向root退出循环
                x = root;
            }
        // x是右节点,镜像处理
        } else { // symmetric
            ...
        }
    }

    setColor(x, BLACK);
}

HashMap插入

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;
    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) {
        // comparableClassFor是判断k所属类是否实现了Comparable接口,实现了则返回k所属类
        // compareComparables是用Comparable接口的方法比较k和pk两个对象
        // 没实现Comparable接口,则用tieBreakOrder方法来判断,或者实现了Comparable接口但是判断结果等于0,还是用tieBreakOrder方法来判断
            if (!searched) {
                // 第一次进入这个else if分支的时候,会去当前节点左右子树都找一遍,找到了返回节点,整个循环只会找一次
                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;
            }
            // hash值和Comparable接口的方法(如果有的话)都对比相等,但是key不相等时,用System.identityHashCode()来进行对比
            dir = tieBreakOrder(k, pk);
        }
        // 上面是查找,找到了会返回节点,到方法外面设置新值,没找到往下继续
        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;
            // 调整红黑树,并调整双向链表
            moveRootToFront(tab, balanceInsertion(root, x));
            return null;
        }
    }
}
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;;) {
        // 父节点为null,说明当前节点是根节点,置为红色,并返回当前节点
        if ((xp = x.parent) == null) {
            x.red = false;
            return x;
        }
        // 父节点是黑色,或父节点是根节点,返回root
        else if (!xp.red || (xpp = xp.parent) == null)
            return root;
        // 父节点是左节点,隐含信息:父节点是红色,祖父节点是黑色
        if (xp == (xppl = xpp.left)) {
            // 叔叔节点是红色
            if ((xppr = xpp.right) != null && xppr.red) {
                // 叔叔和父节点变黑色,祖父变红色
                xppr.red = false;
                xp.red = false;
                xpp.red = true;
                // 以祖父节点作为当前节点继续循环
                x = xpp;
            }
            // 叔叔是黑色
            else {
                // 当前节点是右节点,则左旋,将红节点变左节点
                if (x == xp.right) {
                    root = rotateLeft(root, x = xp);
                    // x,xp,xpp重新赋值
                    xpp = (xp = x.parent) == null ? null : xp.parent;
                }
                if (xp != null) {
                    // 父节点变黑色,祖父变红色,右旋
                    xp.red = false;
                    if (xpp != null) {
                        xpp.red = true;
                        root = rotateRight(root, xpp);
                    }
                }
            }
        }
        // 镜像处理
        else {
            ...
        }
    }
}

HashMap删除

final void removeTreeNode(HashMap<K,V> map, Node<K,V>[] tab,
                          boolean movable) {
    int n;
    // 数组为空,肯定无红黑树,直接返回
    if (tab == null || (n = tab.length) == 0)
        return;
    // 计算数组中的下标
    int index = (n - 1) & hash;
    TreeNode<K,V> first = (TreeNode<K,V>)tab[index], root = first, rl;
    TreeNode<K,V> succ = (TreeNode<K,V>)next, pred = prev;
    // 从双向链表中删除当前节点
    if (pred == null)
        // 当前节点在双向链表的头部,先将tab[index]和first指向双向链表的第二个节点
        tab[index] = first = succ;
    else
        // 当前节点不是双向链表的头部,将前面节点的next指向后面节点
        pred.next = succ;
    if (succ != null)
        // 后面节点不是null,即为当前节点不是尾节点,后面节点的prev指向前面节点
        succ.prev = pred;
    if (first == null)
        // first为null直接返回,只有两种情况,tab[index]一开始为空或从双向链表中删除头节点后tab[index]为空,即为双向链表一开始就是空的,或删除当前节点后为空链表
        return;
    if (root.parent != null)
        // root重新定位到红黑树的根节点(如果当前节点是根节点,上面操作后tab[index]不再指向根节点 tab[index] = first = succ;)
        root = root.root();
    if (root == null
        || (movable
            && (root.right == null
                || (rl = root.left) == null
                || rl.left == null))) {
        // root为null,或root右孩子为null,或root左孩子为null,或root左孩子的左孩子为null,此处未用到去树化阈值UNTREEIFY_THRESHOLD
        // 双链表转单链表,即为红黑树转单链表
        tab[index] = first.untreeify(map);  // too small
        return;
    }
    TreeNode<K,V> p = this, pl = left, pr = right, replacement;
    // 当前节点有两个子节点时
    if (pl != null && pr != null) {
        TreeNode<K,V> s = pr, sl;
        // 找到后继节点s
        while ((sl = s.left) != null) // find successor
            s = sl;
        // p和s交换颜色
        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;
        // p和s交换位置
        if (s == pr) { // p was s's direct parent
            // 两者相邻时,父子关系转换
            p.parent = s;
            s.right = p;
        }
        else {
            // 两者不相邻,交换各自的关系
            TreeNode<K,V> sp = s.parent;
            if ((p.parent = sp) != null) {
                if (s == sp.left)
                    sp.left = p;
                else
                    sp.right = p;
            }
            if ((s.right = pr) != null)
                pr.parent = s;
        }
        // sl肯定是null,因此需将p.left置为null
        p.left = null;
        if ((p.right = sr) != null)
            sr.parent = p;
        if ((s.left = pl) != null)
            pl.parent = s;
        if ((s.parent = pp) == null)
            // p没有父节点时,将root指向s
            root = s;
        else if (p == pp.left)
            pp.left = s;
        else
            pp.right = s;
        if (sr != null)
            // 交换后p的子节点不为空,replacement指向子节点
            replacement = sr;
        else
            // 子节点为空,replacement指向p
            replacement = p;
    }
    // 右子节点为空,左子节点不为空,replacement指向左子节点
    else if (pl != null)
        replacement = pl;
    // 左子节点为空,右子节点不为空,replacement指向右子节点
    else if (pr != null)
        replacement = pr;
    // 子节点为空,replacement指向p
    else
        replacement = p;
    if (replacement != p) {
        // p有子节点,先删除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.left = p.right = p.parent = null;
    }

    // 平衡操作
    TreeNode<K,V> r = p.red ? root : balanceDeletion(root, replacement);

    // p没有子节点,先平衡操作,再删除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)
        moveRootToFront(tab, r);
}

static <K,V> TreeNode<K,V> balanceDeletion(TreeNode<K,V> root,
                                           TreeNode<K,V> x) {
    for (TreeNode<K,V> xp, xpl, xpr;;) {
        // 当前节点为null 或 root 直接返回 root
        if (x == null || x == root)
            return root;
        else if ((xp = x.parent) == null) {
            // 父节点为null,当前节点置为黑色,返回当前节点
            x.red = false;
            return x;
        }
        else if (x.red) {
            // 当前节点是红色时,置为黑色,返回root
            x.red = false;
            return root;
        }
        else if ((xpl = xp.left) == x) {
            // 当前节点是左节点时
            // 兄弟节点为红色时,左旋转换成兄弟节点为黑色的情况,再继续
            if ((xpr = xp.right) != null && xpr.red) {
                xpr.red = false;
                xp.red = true;
                root = rotateLeft(root, xp);
                xpr = (xp = x.parent) == null ? null : xp.right;
            }
            // 兄弟节点为null,以父节点为当前节点重新处理(会有这种情况吗?原始的单子节点的情况下,节点颜色必定是红色,上面已拦截,其他情况也不会转换为这种情况)
            if (xpr == null)
                x = xp;
            else {
                TreeNode<K,V> sl = xpr.left, sr = xpr.right;
                // 两个侄子节点都不为红色时,隐含信息:兄弟节点为黑色
                if ((sr == null || !sr.red) &&
                    (sl == null || !sl.red)) {
                    // 将黑色借给父节点,再以父节点为当前节点继续,此处可能去到else if (x.red)分支
                    xpr.red = true;
                    x = xp;
                }
                // 有一个侄子节点为红色的情况
                else {
                    // 右侄子不为红色时,隐含信息:左侄子为红色
                    if (sr == null || !sr.red) {
                        // 此处多余判断,sl == null 会进入上面分支,sl一定为红色
                        if (sl != null)
                            sl.red = false;
                        xpr.red = true;
                        // 右旋,转化成右侄子为红色的情况
                        root = rotateRight(root, xpr);
                        // xpr指向新的兄弟节点
                        xpr = (xp = x.parent) == null ?
                            null : xp.right;
                    }
                    // 兄弟节点不为null时,其实兄弟节点在这里不会为null,xp父节点也不会为null,sr右侄子也不会为null,且一定是红色
                    if (xpr != null) {
                        xpr.red = (xp == null) ? false : xp.red;
                        if ((sr = xpr.right) != null)
                            // 将父节点的颜色给兄弟节点,将右侄子的颜色设置成黑色
                            sr.red = false;
                    }
                    // xp不会为null
                    if (xp != null) {
                        // 父节点设置为黑色,左旋
                        xp.red = false;
                        root = rotateLeft(root, xp);
                    }
                    x = root;
                }
            }
        }
        // 当前节点时右节点时,镜像处理
        else {
            ...
        }
    }
}
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 216,402评论 6 499
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,377评论 3 392
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 162,483评论 0 353
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,165评论 1 292
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,176评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,146评论 1 297
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,032评论 3 417
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,896评论 0 274
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,311评论 1 310
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,536评论 2 332
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,696评论 1 348
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,413评论 5 343
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,008评论 3 325
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,659评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,815评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,698评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,592评论 2 353

推荐阅读更多精彩内容