打工人必会的HashMap! 上篇

早安,打工人.现在只要去面试无论大厂小厂几乎都包含了HashMap,咱们就来看看HashMap源码.上次我们说到了Hashtable是数组+链表结构,而HashMap则是数组+链表+红黑树结构.

咱们在正式开讲之前需要先知道什么是红黑树

概念

红黑树(R-B Tree),全称Red-Black Tree是一种自平衡二叉查找树,可以在==O(logN)==时间内完成查找,增加,删除等操作.它的每个节点上都存储了颜色标识,可以是红或黑.在进行插入和删除等可能破坏树平衡的操作时,会通过变色,左旋,右旋来完成自平衡.

红黑树的特性:
  1. 每个节点非黑即红
  2. 根节点是黑色
  3. 所有叶子节点都是黑色(NIL节点)
  4. 每个红色节点的两个子节点都是黑色
  5. 从任一节点到其叶子节点的所有路径都包含相同数量的黑色节点

这些特性确定了红黑树的关键性质: 从根到叶子的最长路径不会多于最短路径的两倍长,从而这个树大致上是平衡的.因为插入,删除和查找某个值的最坏情况实际都要求与树的高度成比例,这个高度上的理论上线允许红黑树在最坏的情况下都是高效的,而不同于普通的二叉查找树.

如图即是一个简单的红黑树,其中NIL是叶子节点且是黑色的.红黑树不是一个完美平衡二叉树,但从任一节点到其叶子节点的路径中包含数量相同的黑色节点(即左子树和右子树上的黑色结点层数是相等的),所以我们称红黑树的这种平衡为黑色完美平衡.

上面有讲到红黑树的自平衡的三种操作: 变色 左旋 右旋.

  • 变色

    • 节点颜色由红变黑,或由黑变红
  • 左旋

    • 以某节点为支点,其右子节点变为支点的父节点,右子节点的左节点变为支点的右节点,支点变为右子节点的左节点.
  • 右旋

    • 以某节点为支点,其左子节点变为支点的父节点,左子节点的右子节点变为支点的左子节点,支点变为左子节点的右节点.

以上为红黑树的大概介绍,听着比较迷糊,下面将结合HashMap源码详细讲解红黑树的各种操作.

TreeNode解析

在HashMap中针对红黑树的操作主要是通过TreeNode来进行的

    static final class TreeNode<K, V> extends LinkedHashMap.Entry<K, V> {
        // 父节点
        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;

        TreeNode(int hash, K key, V val, Node<K, V> next) {
            super(hash, key, val, next);
        }

TreeNode继承自LinkedHashMap.Entry,而LinkedHashMap.Entry又继承自Node,TreeNode中保留了Node里的属性,同时增加了prev字段使链表变成了双向的.

    static class Node<K,V> implements Map.Entry<K,V> {
        final int hash;
        final K key;
        V value;
        Node<K,V> next;
    }

下边来看看TreeNode的函数

rotateLeft(TreeNode<K, V> root,TreeNode<K, V> p)

左旋,逆时针旋转支点,使支点的位置被其右子节点取代,而支点本身则变为其右子节点的左节点.

    // 左旋
    static <K, V> TreeNode<K, V> rotateLeft(TreeNode<K, V> root,
                                            TreeNode<K, V> p) {
        TreeNode<K, V> r, pp, rl;
        // p为旋转支点
        // 如果p的右节点不为空则进行左旋
        if (p != null && (r = p.right) != null) {
            // p的右节点的左节点顶替p的右节点位置
            if ((rl = p.right = r.left) != null)
                rl.parent = p;
            // r替代p的位置, 如果p的父节点为空,说明是根节点,设置为黑色
            if ((pp = r.parent = p.parent) == null)
                (root = r).red = false;
                // 用 r 替换 原来的 p
            else if (pp.left == p)
                pp.left = r;
            else
                pp.right = r;
    
            // 再把原来的p 放到 r 的left属性
            r.left = p;
            p.parent = r;
        }
        return root;
    }
rotateRight(TreeNode<K, V> root, TreeNode<K, V> p)

右旋,和左旋相反,顺时针旋转支点,使支点的位置被其左子节点取代,而支点本身变为其左子节点右节点.

    // 右旋
    static <K, V> TreeNode<K, V> rotateRight(TreeNode<K, V> root,
                                             TreeNode<K, V> p) {
        TreeNode<K, V> l, pp, lr;
        // 操作和左旋相反
        if (p != null && (l = p.left) != null) {
            if ((lr = p.left = l.right) != null)
                lr.parent = p;
            if ((pp = l.parent = p.parent) == null)
                (root = l).red = false;
            else if (pp.right == p)
                pp.right = l;
            else
                pp.left = l;
            l.right = p;
            p.parent = l;
        }
        return root;
    }
putTreeVal(HashMap<K, V> map, Node<K, V>[] tab,int h, K k, V v)

红黑树的插入包含两部分操作,一是查找插入的位置,二是节点插入后,红黑树的自平衡.
查找插入的位置比较简单,就是由根节点向下迭代,找寻合适的左右枝一直往下到叶子节点,插入后会进行第二步自平衡.

    // 插入一个节点/
    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;
            // p的hash 大于 要插入的 ,放左边
            if ((ph = p.hash) > h)
                dir = -1;

                // 放右边
            else if (ph < h)
                dir = 1;

                // 若是key相同
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                return p;

            // comparable比较pk和k的大小,如若不能比较或比较不出
            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;
                }

                // 针对hash相同但不能进行comparable比较的统一规则处理,dir要么-1 要么1
                dir = tieBreakOrder(k, pk);
            }

            TreeNode<K, V> xp = p;
            // 如果插入节点所属左右节点不是是叶子就继续往下层迭代
            if ((p = (dir <= 0) ? p.left : p.right) == null) {
                // 维护next,prev,便于树和链表转化
                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;
                // 插入元素后做自平衡调整,同时确保树根root为链表头元素
                moveRootToFront(tab, balanceInsertion(root, x));
                return null;
            }
        }
    }

由插入节点自下而上遍历,根据父节点,叔叔节点和爷爷节点的位置和颜色来确定是否需要变色和旋转,自平衡分为四种情况:

  1. 当前节点是根节点
    • 设置当前节点为黑色
  2. 父节点为黑色或爷爷节点为黑色
    • 无需变色
  3. 父节点为红色且叔叔节点是红色
    • 设置父节点和叔叔节点为黑色,爷爷节点为红色
  4. 父节点为红色且叔叔是null或黑色
    • 设置父节点为黑色,爷爷节点为红色,再进行旋转重订位置
    // 此函数完成整个红黑树位置的调整和颜色的变化
    static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) {

        // root 根节点,  x为迭代节点
        // 默认设置叶子节点为红色
        x.red = true;
        for (TreeNode<K, V> xp, xpp, xppl, xppr; ; ) {
            // 1.如果x没有parent,说明是根节点,则设为黑色,并返回
            if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            }

            // 2.如果x的父节点是黑色或x的爷爷是根节点 正常返回
            else if (!xp.red || (xpp = xp.parent) == null)
                // 返回 原来的根节点
                return root;

            // 3.父节点为红色且是父节点为爷爷的左节点
            if (xp == (xppl = xpp.left)) {

                // 3.1. 叔叔节点为红色
                if ((xppr = xpp.right) != null && xppr.red) {

                    // 设置父节点和叔叔节点为 黑色,爷爷节点为红色
                    xppr.red = false;
                    xp.red = false;
                    xpp.red = true;
                    // 再赋值当前迭代节点为爷爷节点,向上迭代平衡树
                    x = xpp;
                } else {
                    // 3.2.叔叔节点为null或黑色

                    // 当前节点和父节点,爷爷节点不是一条直线,则先左旋调整
                    // 如果当前节点是 父 节点的 right节点
                    if (x == xp.right) {
                        // 左旋,以父节点为支点
                        root = rotateLeft(root, x = xp);
                        xpp = (xp = x.parent) == null ? null : xp.parent;
                    }

                    // 2. 再右旋
                    if (xp != null) {
                        xp.red = false;
                        if (xpp != null) {
                            xpp.red = true;
                            root = rotateRight(root, xpp);
                        }
                    }
                }
            } 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);
                        }
                    }
                }
            }
        }
    }
root()
    // 获取当前节点所属数的根节点
    final TreeNode<K, V> root() {
        // 向上父遍历
        for (TreeNode<K, V> r = this, p; ; ) {
            if ((p = r.parent) == null)
                return r;
            r = p;
        }
    }
moveRootToFront(Node<K, V>[] tab, TreeNode<K, V> root)
    // 确保给定根节点root是红黑树的根节点
    static <K, V> void moveRootToFront(Node<K, V>[] tab, TreeNode<K, V> root) {
        int n;
        if (root != null && tab != null && (n = tab.length) > 0) {
            int index = (n - 1) & root.hash;
            TreeNode<K, V> first = (TreeNode<K, V>) tab[index];
            // 若不是第一个节点则调整链表
            if (root != first) {
                // root的next指向原来的根节点,原来的根节点的prev指向root
                // root的next的prev指向root的prev,root的prev指向指向root的next
                // 即把root移除原位置并顶替头
                Node<K, V> rn;
                tab[index] = root;
                TreeNode<K, V> rp = root.prev;
                if ((rn = root.next) != null)
                    ((TreeNode<K, V>) rn).prev = rp;
                if (rp != null)
                    rp.next = rn;
                if (first != null)
                    first.prev = root;
                root.next = first;
                root.prev = null;
            }
            // 检测红黑树不变性
            assert checkInvariants(root);
        }
    }
    // 递归检测红黑树不变性
    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;
        if (tb != null && tb.next != t)
            return false;
        if (tn != null && tn.prev != t)
            return false;
        if (tp != null && t != tp.left && t != tp.right)
            return false;
        if (tl != null && (tl.parent != t || tl.hash > t.hash))
            return false;
        if (tr != null && (tr.parent != t || tr.hash < t.hash))
            return false;
        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;
    }
find(int h, Object k, Class<?> kc)

查找元素也比较简单

  1. 以当前节点为跟根节点开始查找
  2. 若当前节点为空,则返回null
  3. 若当前节点不为空,用当前节点的hash和key根要查找的key作比较
  4. 根据比较结果选择左枝或右枝继续向下层迭代
    // 以当前节点为根节点向下 查找 指定hash和key,class的元素
    final TreeNode<K, V> find(int h, Object k, Class<?> kc) {
        TreeNode<K, V> p = this;
        // 向下遍历
        do {
            int ph, dir;
            K pk;
            TreeNode<K, V> pl = p.left, pr = p.right, q;
            if ((ph = p.hash) > h)  // 1.在left节点下
                p = pl;
            else if (ph < h)  // 2.在right节点下
                p = pr;
            else if ((pk = p.key) == k || (k != null && k.equals(pk)))
                // 3.hash和根节点相同,且k也和根节点k相同
                return p;

            // 执行到这里说明hash相同,但当前迭代的pk和要查找元素的k不同
            else if (pl == null) // 4.如果左节点为空,p = right节点,进行下轮迭代
                p = pr;
            else if (pr == null) // 5.如果right节点为null,p = left节点,进行下轮迭代
                p = pl;
            else if ((kc != null ||
                    (kc = comparableClassFor(k)) != null) &&
                    (dir = compareComparables(kc, k, pk)) != 0)
                // 6.这里说明左右节点都不为空,则进行comparable比较pk和k的大小
                p = (dir < 0) ? pl : pr;
            else if ((q = pr.find(h, k, kc)) != null)
                // 7.这里说明无法comparable比较 或 比较后还是相等,则递归从右子节点向下找,如果匹配到了直接返回
                return q;
            else
                // 8.若右子节点找不到,则从左子节点开始下轮迭代
                p = pl;
        } while (p != null);
        return null;
    }
getTreeNode(int h, Object k)
    // 从根节点开始查找指定元素
    final TreeNode<K, V> getTreeNode(int h, Object k) {
        return ((parent != null) ? root() : this).find(h, k, null);
    }
tieBreakOrder(Object a, Object b)
   // 确保在向红黑树插入hashCode相同元素的时候,插入顺序一致的
   // 插入顺序的规则指定
    static int tieBreakOrder(Object a, Object b) {
        int d;
        if (a == null || b == null ||
                (d = a.getClass().getName().
                        compareTo(b.getClass().getName())) == 0)
            // 对比两个对象内存地址hashCode, 小或等于 都是 -1
            d = (System.identityHashCode(a) <= System.identityHashCode(b) ?
                    -1 : 1);
        return d;
    }
void treeify(Node<K, V>[] tab)

此函数实现链表转红黑树结构,实际上维护了两层关系, 通过nextprev属性来维护链表,parent,left,rightred来维护红黑树.

// 链表转为红黑树结构
final void treeify(Node<K, V>[] tab) {
    TreeNode<K, V> root = null;
    // 遍历这个 TreeNode的 链表
    for (TreeNode<K, V> x = this, next; x != null; x = next) {
        next = (TreeNode<K, V>) x.next;
        x.left = x.right = null;
        // 如果父节点为null,设置父节点
        if (root == null) {
            x.parent = null;
            // 根节点是 黑色的
            x.red = false;
            // 设置为树根
            root = x;
        } 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;
                if ((ph = p.hash) > h)
                    dir = -1;
                else if (ph < h)
                    dir = 1;

                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 计算该元素是在节点的left上还是在right上 (小的在left上,大的在right上)
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    // 若left或right为null,则创建
                    // 设置当前节点的父类为根节点
                    x.parent = xp;
                    if (dir <= 0)
                        xp.left = x;
                    else
                        xp.right = x;
                    // 调整平衡
                    root = balanceInsertion(root, x);
                    break;
                }
            }
        }
    }

   // 这里确保 这个红黑树的根节点root 是 链表的第一个元素
    moveRootToFront(tab, root);
}
untreeify(HashMap<K, V> map)

红黑树退化为链表,通过维护的next属性完成

    // 树转链表
    final Node<K, V> untreeify(HashMap<K, V> map) {
        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;
    }
removeTreeNode(HashMap<K, V> map, Node<K, V>[] tab,boolean movable)

删除和插入一样有两个步骤,第一查找需要删除的元素,第二删除后自平衡.第一步就不用说了,和查找操作一样.
当不存在目标节点时忽略本次操作,若存在,删除后就需要做自平衡了,删除节点时我们还需要找一个替换节点来替代删除节点的位置,不然删除后就节点树就断开了,如果删除节点没有子节点则不需要替换.

后继节点: 右子树最左子节点.

    // 删除指定节点
    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 = succ;
        else
            // 上一个节点不为空,则将pred.next 指向 当前节点.next (即删除当前节点)
            pred.next = succ;

        if (succ != null)
            succ.prev = pred;
        if (first == null)
            return;

        // 根节点存在父节点,说明不是根节点,调用root()确保是根节点
        if (root.parent != null)
            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为要删除节点  replacement 为删除后用于替代的节点(从左右枝中"选拔")
        TreeNode<K, V> p = this, pl = left, pr = right, replacement;
        if (pl != null && pr != null) {
            TreeNode<K, V> s = pr, sl;
            // 1. 若删除节点有两个子节点,用后继节点(即大于删除节点最小节点)替代删除节点
            while ((sl = s.left) != null) // find successor
                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
                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;
            }
            p.left = null;
            // 最小右子节点肯定没有左子节点,但不一定没有右子节点
            if ((p.right = sr) != null)
                sr.parent = p; // 赋值右子节点
            if ((s.left = pl) != null)  //  删除节点的左枝 赋值给 后继节点
                pl.parent = s;

            // 把后继节点 指向 删除节点父节点的子节点
            if ((s.parent = pp) == null)
                root = s;
            else if (p == pp.left)
                pp.left = s;
            else
                pp.right = s;

            // sr不为空,replacement为 后继节点的右节点
            if (sr != null)
                replacement = sr;
            else
                // 否则为删除节点
                replacement = p;

            // 2.删除节点只有一个子节点,用子节点替换删除节点
        } else if (pl != null)
            replacement = pl;
        else if (pr != null)
            replacement = pr;
        else
            // 3.若删除节点不存在子节点,则直接删除
            replacement = p;

        // replacement 要么为替代节点的子节点要么就是删除节点

        // replacement!=p的情况
        // p有子节点,或 p有后继节点
        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;
        }
        // p 若为红色删除无影响, 若为黑色则需要平衡
        TreeNode<K, V> r = p.red ? root : balanceDeletion(root, replacement);

        // p没有左右节点 或  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);
    }

以上图为例演示
删除后自平衡比较复杂有多种情况

  1. 替换节点是根或红色
    • 直接把替换节点设置为黑色
  2. 替换节点是黑色,且替换节点的兄弟节点是红色
    • 将兄弟节点设为黑,父节点设为红,旋转父节点,将情况转化
  3. 替换节点是黑色,且兄弟节点和兄弟的子节点均为黑色
    • 将兄弟节点设为红色,将父节进行旋转调色,转化情况
  4. 替换节点是黑色,且兄弟节点是黑色,兄弟节点的右节点是黑色
    • 兄弟节点的左节点设为黑色,兄弟节点设为红色,以兄弟节点为支点旋转,转化情况
  5. 替换节点是黑色,且兄弟节点是黑色,兄弟节点的右节点是红色
    • 兄弟节点颜色设置成父节点的颜色,兄弟节点的右节点设置成黑色,父节点设置成黑色,以父节点为支点旋转
 // 删除的节点为黑色则需要平衡
    static <K, V> TreeNode<K, V> balanceDeletion(TreeNode<K, V> root,
                                                 TreeNode<K, V> x) {
        for (TreeNode<K, V> xp, xpl, xpr; ; ) {
            if (x == null || x == root)
                return root;
            else if ((xp = x.parent) == null) {
                x.red = false;
                return x;
            } else if (x.red) {
                // 1. x为红色
                //处理 变为黑色
                x.red = false;
                return root;
            } else if ((xpl = xp.left) == x) {
                // 2. 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;
                }
                // 不存在兄弟节点,则指向父节点向上处理
                if (xpr == null)
                    x = xp;
                else {
                    // 3.x兄弟为黑色,且兄弟的左右子节点均为黑色
                    // 处理: 兄弟节点变红,指向父节点继续自平衡
                    TreeNode<K, V> sl = xpr.left, sr = xpr.right;
                    if ((sr == null || !sr.red) &&
                            (sl == null || !sl.red)) {
                        // 把兄弟设为红
                        xpr.red = true;
                        //  x指向xp进行上溯
                        x = xp;
                    } else {
                        // 4.兄弟的右节点是黑色,左节点是红色
                        // 处理: 左节点设为黑色,兄弟节点设为红色,以兄弟节点为支点右旋,继续自平衡
                        if (sr == null || !sr.red) {
                            if (sl != null)
                                sl.red = false;

                            xpr.red = true;
                            root = rotateRight(root, xpr);
                            xpr = (xp = x.parent) == null ?
                                    null : xp.right;
                        }

                        if (xpr != null) {
                            // 5.兄弟节点的右节点是红色
                            // 处理: 1.兄弟节点颜色设置成父节点的颜色

                            // 2.右孩子设置成黑色
                            xpr.red = (xp == null) ? false : xp.red;
                            if ((sr = xpr.right) != null)
                                sr.red = false;
                        }
                        // 父节点存在设置为黑色
                        if (xp != null) {
                            xp.red = false;
                            // 以父节点进行左旋
                            root = rotateLeft(root, xp);
                        }
                        // 替换节点指向根节点,平衡结束
                        x = root;
                    }
                }
            } else { // 和上述对称
                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;
                    }
                }
            }
        }
    }
split(HashMap<K, V> map, Node<K, V>[] tab, int index, int bit)

split函数是将旧数组转移到新数组,分为两个步骤.

  1. 迭代链表,根据e.hash & bit == 0重新计算它在树链表的位置, 如果 e.hash & bit == 0记入低位区树链表lo. 若e.hash & bit != 0,记入高位区树链表hi.
  2. 根据低位区和高位区元素的数量来进行树的转换或去树化,同时低位区元素位置不变,高位区元素会放在新数组中的新位置上tab[index + bit]
    // 这个函数是对树的拆分,构成 会在HashMap rehash的时候调用
    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代表扩容的二进制位(数值为扩容前的容量大小)
            // 0 说明是在低位树,原则位置
            if ((e.hash & bit) == 0) {

                // 如果当前节点没有尾节点,就设置为头节点
                if ((e.prev = loTail) == null)
                    loHead = e;
                else
                    // 把当前节点设置上一个尾节点的下一个节点
                    loTail.next = e;

                // 设置尾节点
                loTail = e;

                ++lc;
            } else {
                // 高位树上的处理
                // 这里把原来的尾节点赋值给e.prev,同时判断是否尾节点为null,说明没有尾节点,再设置e为头节点
                if ((e.prev = hiTail) == null)
                    hiHead = e;
                else
                    // 说明有尾节点,把尾节点的下一个设置成e
                    hiTail.next = e;

                // 同时 把 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);
            }
        }
    }


以上为HashMap中TreeNode红黑树部分的相关介绍,下篇文章我们会详细介绍HashMap其余Api和实现原理.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值