共同学习Java源代码-数据结构-HashMap(十五)

从这篇文章开始到HashMap系列的结束 都会讲一个内容 那就是HashMap的TreeNode内部类 HashMap每个桶的链表的长度达到8 再想给链表增加元素 就要将链表改成二叉树 这个TreeNode就是红黑树的实现

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

这个类是静态final的 继承自LinkedHashMap的Entry

TreeNode<K,V> parent;  
TreeNode<K,V> left;
TreeNode<K,V> right;
TreeNode<K,V> prev;

这四个属性分别是父节点,左节点,右节点和前一个节点

boolean red;

这个属性是确定是红树节点 还是黑树节点


        final TreeNode<K,V> root() {
            for (TreeNode<K,V> r = this, p;;) {
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }

这个是求根节点的方法 就是不停地获取parent节点 直到parent为空的时候


        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) {
                    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);
            }
        }

这个方法是将根节点放入哈希桶的方法 

先定义临时变量n 

判断传入的根元素不为空 且 传入的哈希桶数组不为空 且传入的哈希桶数组的长度大于0

如果上述条件成立 计算一下传入的根元素应该放在哈希桶的哪个下标处 计算方法还是那个 用数组长度减一的值和根元素的哈希值进行按位与的结果 作为下标 TreeNode直接继承自LinkedHashMap的Entry 而这个Entry又继承自HashMap的Node 所以TreeNode其实和Node是有继承关系的

然后获取哈希桶数组的指定下标元素 判断是否和根元素为同一元素 

如果哈希桶数组的指定下标元素不是这个根元素 那就先创建普通节点类变量rn 

然后将哈希桶数组制定下标赋为传入的根元素 

然后获取根元素的上一个元素prev 赋给变量rp 

判断根元素的下一个元素不为空 将这个下一个元素的prev节点指向刚才获取的上一个元素prev

然后判断根元素的上一个元素不为空的话 就将根元素的上一个元素的next节点指向根元素的下一个节点 

挺绕口的 其实就是一旦链表转换成了红黑树 实际上为了方便遍历 这颗红黑树仍然保留链表顺序的 刚才的做法就是将跟元素从之前的链表顺序中剔除 让根元素之前之后的元素建立"连接" 可以理解为将根元素从链表中拆出来放在第一位 

接下来根元素的下一个元素开水奇偶first 根元素的上一个元素置空

最后断言一下根元素的前后左右元素 都是不是正确的 具体代码如下

        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;
        }
    }

这个方法是断言传入的树元素

先判断如果上一个元素不为空 且上一个元素的下一个元素不是本元素 就返回false 换句话说就是本元素和上一个元素之间的链条断了

再判断下一个元素的上一个元素不是本元素 就返回false 这个是本元素和下一个元素之间的链条断了

再判断这个元素不是父元素的左元素且也不是右元素 就返回false 这个是本元素和父元素的链条断了

再判断左元素的父元素不是本元素 或 左元素的哈希值大于本元素的哈希值 就返回false 这个是本元素和本元素的左元素的链条断了

再判断右元素的父元素不是本元素 或 右元素的哈希值小于本元素的哈希值 就返回false 这个是本元素和本元素的右元素的链条断了

再判断如果左元素 本元素 右元素都是红的 就返回false

然后先后递归判断左元素和右元素是否合法

最后返回true

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值