Java 8 HashMap(八)—— putTreeVal方法和balanceInsertion方法以及左旋和右旋

说明看注释

一、putTreeVal方法

        final TreeNode<K, V> putTreeVal(HashMap<K, V> map, Node<K, V>[] tab, int h, K k, V v) {
            //kc key的class
            Class<?> kc = null;
            //已搜索
            boolean searched = false;
            //获得根节点
            TreeNode<K, V> root = (parent != null) ? root() : this;
            //从根节点开始遍历
            for (TreeNode<K, V> p = root; ; ) {
                //p 当前循环的节点
                //ph p的哈希值,pk p的key
                int dir, ph = p.hash;
                K pk = p.key;
                //比较哈希值大小,判断要插入的位置
                if (ph > h) {
                    dir = -1;
                } else if (ph < h) {
                    dir = 1;
                    //如果key相等
                } else if (Objects.equals(k, pk)) {
                    //直接返回p
                    return p;
                    //如果哈希值相等但是key不相等
                } else if ((kc == null && (kc = comparableClassFor(k)) == null) || (dir = compareComparables(kc, k, pk)) == 0) {
                    //key不是可比较可类,或者pk与key比较后相等
                    //当还未进入过此次判断时,进行这次判断(只能进入一次)
                    if (!searched) {
                        //ch p节点的左右子节点,q 为ch节点通过h,k,kc从其子节点中找到的节点
                        TreeNode<K, V> q, ch;
                        //将searched设置为true防止下次进入
                        searched = true;
                        //将p节点下的左右子树遍历一遍
                        //如果ch节点不为空,q节点不为空,说明找到了
                        if (((ch = p.left) != null && (q = ch.find(h, k, kc)) != null) ||
                                ((ch = p.right) != null && (q = ch.find(h, k, kc)) != null)) {
                            //返回q节点
                            return q;
                        }
                    }
                    //这里表示ch节点为空或者q节点为空,使用tieBreakOrder方法进行比较
                    dir = tieBreakOrder(k, pk);
                }
                //到此处说明没有找到节点,当前是插入操作
                //将xp节点指向q节点
                TreeNode<K, V> xp = p;
                //根据dir的值,获取p的左节点或者右节点,并由p节点指向
                if ((p = (dir <= 0) ? p.left : p.right) == null) {
                    //当当前要插入的节点为null时才可到此处
                    //注:TreeNode不仅维护了节点红黑树关系还维护了链表关系
                    //获得当前节点的后节点xpn(可以为null)
                    Node<K, V> xpn = xp.next;
                    //用传入的h,k,v生成一个新的TreeNode,并且将xpn节点插入其尾部
                    TreeNode<K, V> x = map.newTreeNode(h, k, v, xpn);
                    //根据dir的值放入相应的位置
                    if (dir <= 0) {
                        xp.left = x;
                    } else {
                        xp.right = x;
                    }
                    //再将x节点设置为xp节点的后节点
                    //每个插入到红黑的节点也会插入到链表尾部
                    xp.next = x;
                    //将xp节点设置为x节点的前节点和父节点
                    x.parent = x.prev = xp;
                    if (xpn != null) {
                        //当xpn不为null时,将x节点设置为xpn节点的前节点
                        ((TreeNode<K, V>) xpn).prev = x;
                    }
                    //平衡红黑树,并保证平衡后的根节点放到数组中
                    moveRootToFront(tab, balanceInsertion(root, x));
                    return null;
                }
            }
        }

二、balanceInsertion方法

        static <K, V> TreeNode<K, V> balanceInsertion(TreeNode<K, V> root, TreeNode<K, V> x) {
            //x 当前添加的节点
            //root 根节点
            //设置x节点为红色(红黑树当前节点默认为红色)
            //如果设置为黑色必定会违反红黑数特性5
            x.red = true;
            //xp x节点的父节点
            //xpp xp节点的父节点
            //xppl xpp节点的左节点
            //xppr xpp节点的右节点
            for (TreeNode<K, V> xp, xpp, xppl, xppr; ; ) {
                //若插入的是根节点,设置为黑色直接返回
                //满足红黑树特性2
                if ((xp = x.parent) == null) {
                    x.red = false;
                    return x;
                    //xp节点为黑色不会影响红黑数的结构直接返回根节点
                    //xpp节点为空说明xp节点为根节点,直接返回跟节点
                } else if (!xp.red || (xpp = xp.parent) == null) {
                    return root;
                }
                //如果xp节点等于xppl节点
                if (xp == (xppl = xpp.left)) {
                    //xppr节点不为空且xppr节点是红色的
                    if ((xppr = xpp.right) != null && xppr.red) {
                        //将xppr节点和xp节点设置为黑色
                        xppr.red = false;
                        xp.red = false;
                        //将xpp节点设置为红色
                        xpp.red = true;
                        //将x节点的指针指向xpp节点
                        //此处是因为xpp节点向下已经没有问题了,但xpp节点之上肯会有问题
                        //指向xpp节点是为了检查xpp节点之上的节点
                        x = xpp;
                    } else {
                        //xppr节点为空或xppr节点为黑色
                        if (x == xp.right) {
                            //如果x节点是xp节点的右节点
                            //进行左旋操作,将x节点替换为xp节点
                            root = rotateLeft(root, x = xp);
                            //给xpp节点赋值
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        //如果xp节点不为空
                        if (xp != null) {
                            //xp节点变为黑色
                            xp.red = false;
                            //如果xpp节点不为空
                            if (xpp != null) {
                                //xpp节点变为红色
                                xpp.red = true;
                                //进行右旋操作
                                root = rotateRight(root, xpp);
                            }
                        }
                    }
                    //如果xp节点为xpp节点的右节点
                } else {
                    //如果xppl节点不为空且xppl节点是红色的
                    if (xppl != null && xppl.red) {
                        //将xppl节点和xp节点修改为黑色
                        xppl.red = false;
                        xp.red = false;
                        //xpp节点修改为红色
                        xpp.red = true;
                        //将x节点的指针指向xpp节点
                        //此处是因为xpp节点向下已经没有问题了,但xpp节点之上肯会有问题
                        //指向xpp节点是为了检查xpp节点之上的节点
                        x = xpp;
                    } else {
                        //如果xppl节点为空或者xppl节点为黑色
                        //如果x节点是xp节点的左节点
                        if (x == xp.left) {
                            //进行右旋操作,将x节点替换为xp节点
                            root = rotateRight(root, x = xp);
                            //给xpp节点赋值
                            xpp = (xp = x.parent) == null ? null : xp.parent;
                        }
                        //如果xp节点不为空
                        if (xp != null) {
                            //将xp节点设置为黑色
                            xp.red = false;
                            //如果xpp节点不为空
                            if (xpp != null) {
                                //将xpp节点设置为红色的
                                xpp.red = true;
                                //进行左旋操作
                                root = rotateLeft(root, xpp);
                            }
                        }
                    }
                }
            }
        }

三、rotateLeft和rotateRight

        static <K, V> TreeNode<K, V> rotateLeft(TreeNode<K, V> root, TreeNode<K, V> p) {
            //r p节点的右节点
            //pp p节点的父节点
            //rl r节点的左节点
            TreeNode<K, V> r, pp, rl;
            //p节点不为空,且r节点不为空
            if (p != null && (r = p.right) != null) {
                //rl节点变为p节点的右节点,如果rl节点不为空,则p节点变为rl的父节点
                if ((rl = p.right = r.left) != null) {
                    rl.parent = p;
                }
                //pp节点变为r节点的父节点,若pp节点为空,则r节点成为根节点
                if ((pp = r.parent = p.parent) == null) {
                    (root = r).red = false;
                    //若pp节点不为空,则判断p节点在pp节点的那一边,把r节点放在相应位置上
                } else if (pp.left == p) {
                    pp.left = r;
                } else {
                    pp.right = r;
                }
                //p节点变成r节点的左节点
                r.left = p;
                //r节点变成p节点的父节点
                p.parent = r;
            }
            return root;
        }

        static <K, V> TreeNode<K, V> rotateRight(TreeNode<K, V> root, TreeNode<K, V> p) {
            //l p节点的左节点
            //pp p节点的父节点
            //lr l节点的右节点
            TreeNode<K, V> l, pp, lr;
            //p节点不为空,l节点不为空
            if (p != null && (l = p.left) != null) {
                //lr节点变为p节点的左节点,如果lr节点不为空,则p节点变为lr节点的父节点
                if ((lr = p.left = l.right) != null) {
                    lr.parent = p;
                }
                //pp节点变为l的父节点,如果pp节点为空,则l节点变为跟节点
                if ((pp = l.parent = p.parent) == null) {
                    (root = l).red = false;
                    //如果pp节点不为空,则判断p节点在pp节点的那一边,把l节点放入对应的位置
                } else if (pp.right == p) {
                    pp.right = l;
                } else {
                    pp.left = l;
                }
                //p节点变为l节点的右节点
                l.right = p;
                //l节点变为p节点的父节点
                p.parent = l;
            }
            return root;
        }

上一篇:网安小白入门-【VM部署XP系统】


下一篇:xpath匹配多个css样式的class