HashMap源码分析系列-HashMap中的红黑树

这篇博客深入解析HashMap中的红黑树源码,包括TreeNode分析、root、split、treeify、moveRootToFront、find、untreeify、putTreeVal和balanceInsertion方法。文章强调理解红黑树特性及保持平衡的重要性,并提供了相关参考资料。
摘要由CSDN通过智能技术生成

HashMap红黑树源码深度解析

前言

HaspMap源码深度分析(一)
在上篇博客的分析中,没有涉及到红黑树的分析,因此这里统一对HashMap中的红黑树进行统一分析它的源码。在阅读本篇博客前,是需要提前了解一下红黑树的,主要有红黑树的特性、如何维持红黑树的平衡等。

源码分析

TreeNode分析

hashMap转换为树的过程,首先要将单链表转换为双链表,那节点之间的关系则是靠继承到的LinkedHashMap.Entry中的next来维护的。在经过转换双链表之后,再转换为树,那转换为树的过程并没有删掉节点之间的关联关系,因此HashMap中的红黑树,也保留着双链表的关系。

 /**
     * 红黑树,这里继承了LinkedHashMap.Entry, 里面维护了next来指向下一个节点,
     * 说明由双链表转换为红黑树后,仍然保留了双向链表的关系。
     */
    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);
        }
     }

root方法分析

root方法的作用是获取红黑树的根节点,比较简单。

        /**
         * 获取红黑树的根节点。
         */
        final TreeNode<K,V> root() {
   
            /*
             * this实际是指向调用root()方法的节点,如:e.root(), 那么this指向的就是e。
             * 这里的就是从当前节点开始,一直向上遍历,知道遍历节点的父节点为空,那么这个节点
             * 就是根节点了。 
             */
            for (TreeNode<K,V> r = this, p;;) {
   
                if ((p = r.parent) == null)
                    return r;
                r = p;
            }
        }

split方法分析

split方法主要发生在resize方法内,用于将旧数组中的红黑树的迁移处理。

        /**
         * 将红黑树进行拆分
         */
        final void split(HashMap<K,V> map, Node<K,V>[] tab, int index, int bit) {
   
            //---------------------------------这一块的具体的逻辑与resize中的分析差不多 start************************************
            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;
                if ((e.hash & bit) == 0) {
   
                    if ((e.prev = loTail) == null)
                        loHead = e;
                    else
                        loTail.next = e;
                    loTail = e;
                    ++lc;
                }
                else {
   
                    if ((e.prev = hiTail) == null)
                        hiHead = e;
                    else
                        hiTail.next = e;
                    hiTail = e;
                    ++hc;
                }
            }
            //---------------------------------这一块的具体的逻辑与resize中的分析差不多 start************************************

            /*
             * 低位的头节点不位空处理流程
             */
            if (loHead != null) {
   
                //如果低位树节点的个数小于6,那么就将红黑树转换位链表
                if (lc <= UNTREEIFY_THRESHOLD)
                    tab[index] = loHead.untreeify(map);
                //如果低位树节点的个数大于6,那么将新数组的索引指向拆分后的树的头节点,并再次进行红黑树树化的处理,以维持红黑树的平衡
                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 {
   
                    tab[index + bit] = hiHead;
                    if (loHead != null)
                        hiHead.treeify(tab);
                }
            }
        }

treeify方法分析

HashMap由链表转换树的过程,主要的思想就是,从根节点遍历红黑树,以找到新增节点的插入位置并将新增节点插入到红黑树了,最后需要维护红黑树的平衡。

       /**
         * 将双向链表转换位红黑树。
         * 这里需要知道红黑树的一些特性:
         *  (1)每个节点或者是黑色,或者是红色。
         *  (2)根节点是黑色。
         *  (3)每个叶子节点(NIL)是黑色。 [注意:这里叶子节点,是指为空(NIL或NULL)的叶子节点!]
         *  (4)如果一个节点是红色的,则它的子节点必须是黑色的。
         *  (5)从一个节点到该节点的子孙节点的所有路径上包含相同数目的黑节点。
         */
        final void treeify(Node<K,V>[] tab) {
   
            //root表示根节点
            TreeNode<K,V> root = null;
            /*
             * 这里需要搞清楚,this实际指向的是调用treeify方法的节点,
             * x: 指向当前节点
             * next: 指向x的下一个节点
             */
            for 
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值