HashMap链表与红黑树转换详解

 更新时间:2023年11月02日 09:00:40   作者:Heloise_yangyuchang  
这篇文章主要介绍了HashMap链表与红黑树转换详解,HashMap是Java中的一种数据结构,它实现了Map接口,提供了键值对的存储和检索功能,它基于哈希表的原理,通过将键映射到哈希表中的位置来存储和获取值,从而实现了快速的查找和插入操作,需要的朋友可以参考下

链表转换为红黑树

                //此处遍历链表
                for (int binCount = 0; ; ++binCount) {
                    //遍历到链表最后一个节点
                    if ((e = p.next) == null) {
                        p.next = newNode(hash, key, value, null);
                        //如果链表元素个数大于等于TREEIFY_THRESHOLD
                        if (binCount >= TREEIFY_THRESHOLD - 1) // -1 for 1st
                            //红黑树转换逻辑
                            treeifyBin(tab, hash);
                        break;
                    }
                    if (e.hash == hash &&
                        ((k = e.key) == key || (key != null && key.equals(k))))
                        break;
                    p = e;
                }
    
    static final int MIN_TREEIFY_CAPACITY = 64;
    static final int UNTREEIFY_THRESHOLD = 6;
    /**
     * Replaces all linked nodes in bin at index for given hash unless
     * table is too small, in which case resizes instead.
     */
    final void treeifyBin(Node<K,V>[] tab, int hash) {
        int n, index; Node<K,V> e;
        if (tab == null || (n = tab.length) < MIN_TREEIFY_CAPACITY)
            resize();
        else if ((e = tab[index = (n - 1) & hash]) != null) {
            TreeNode<K,V> hd = null, tl = null;
            do {
                TreeNode<K,V> p = replacementTreeNode(e, null);
                if (tl == null)
                    hd = p;
                else {
                    p.prev = tl;
                    tl.next = p;
                }
                tl = p;
            } while ((e = e.next) != null);
            if ((tab[index] = hd) != null)
                hd.treeify(tab);
        }
    }

可以看到在treeifyBin中并不是简单地将链表转换为红黑树,而是先判断table的长度是否大于64,如果小于64,就通过扩容的方式来解决,避免红黑树结构化. 个人觉得链表长度大于8有两种情况:

  • table长度足够,hash冲突过多
  • table长度太小,但是在计算table下标的时候,导致很多hash不一致的key计算的下标一致

所以扩容后链表长度变短,读写效率自然提高。另外,扩容相对于转换为红黑树的好处在于可以保证数据结构更简单。并不是链表长度超过8就一定会转换成红黑树,而是先尝试扩容。

红黑树转换为链表

基本思想是当红黑树中的元素减少并小于一定数量时,会切换回链表。

而元素减少有两种情况:

  • 调用map的remove方法删除元素
  • resize的时候,对红黑树进行了拆分

1、hashMap的remove方法,会进入到removeNode方法,找到要删除的节点,并判断node类型是否为treeNode,然后进入删除红黑树节点逻辑的removeTreeNode方法中,该方法有关解除红黑树结构的分支如下

 //判断是否要解除红黑树的条件
if (root == null || root.right == null ||
                (rl = root.left) == null || rl.left == null) {
                tab[index] = first.untreeify(map);  // too small
                return;
   }

此时是通过红黑树根节点及其子节点是否为空来判断,而满足该条件的最大红黑树结构如下:

在这里插入图片描述

节点数为10,大于 UNTREEIFY_THRESHOLD(6),但是根据该方法的逻辑判断,是需要转换为链表的

2、resize的时候,判断节点类型,如果是链表,则将链表拆分,如果是TreeNode,则执行TreeNode的split方法分割红黑树,而split方法中将红黑树转换为链表的分支如下

//在这之前的逻辑是将红黑树每个节点的hash和一个bit进行&运算,
//根据运算结果将树划分为两棵红黑树,lc表示其中一棵树的节点数
if (lc <= UNTREEIFY_THRESHOLD)
                    tab[index] = loHead.untreeify(map);
                else {
                    tab[index] = loHead;
                    if (hiHead != null) // (else is already treeified)
                        loHead.treeify(tab);
                }

这里才用到了 UNTREEIFY_THRESHOLD 的判断,当红黑树节点元素小于等于6时,才调用untreeify方法转换回链表

总结

1、hashMap并不是在链表元素个数大于8就一定会转换为红黑树,而是先考虑扩容,扩容达到默认限制后才转换

2、hashMap的红黑树不一定小于6的时候才会转换为链表,而是只有在resize的时候才会根据 UNTREEIFY_THRESHOLD 进行转换。

到此这篇关于HashMap链表与红黑树转换详解的文章就介绍到这了,更多相关HashMap链表转换内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

相关文章

  • Idea进行pull的时候Your local changes would be overwritten by merge.

    Idea进行pull的时候Your local changes would be

    这篇文章主要介绍了Idea进行pull的时候Your local changes would be overwritten by merge.具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-11-11
  • SpringBoot项目中控制台日志的保存配置操作

    SpringBoot项目中控制台日志的保存配置操作

    这篇文章主要介绍了SpringBoot项目中控制台日志的保存配置操作,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-06-06
  • SpringMVC之@InitBinder注解详解

    SpringMVC之@InitBinder注解详解

    这篇文章主要介绍了SpringMVC之@InitBinder注解详解,springmvc并不是能对所有类型的参数进行绑定的,如果对日期Date类型参数进行绑定,就会报错IllegalStateException错误,需要的朋友可以参考下
    2024-01-01
  • Spring Boot通过Junit实现单元测试过程解析

    Spring Boot通过Junit实现单元测试过程解析

    这篇文章主要介绍了Spring Boot通过Junit实现单元测试过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-01-01
  • Java实现中国象棋游戏

    Java实现中国象棋游戏

    这篇文章主要为大家详细介绍了Java实现中国象棋游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2022-05-05
  • 话说Spring Security权限管理(源码详解)

    话说Spring Security权限管理(源码详解)

    本篇文章主要介绍了话说Spring Security权限管理(源码详解) ,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-02-02
  • 解决kafka消息堆积及分区不均匀的问题

    解决kafka消息堆积及分区不均匀的问题

    这篇文章主要介绍了解决kafka消息堆积及分区不均匀的问题,具有很好的参考价值,希望对大家有所帮助。如有错误或未考虑完全的地方,望不吝赐教
    2021-09-09
  • IDEA无法识别相关module模块问题的解决过程

    IDEA无法识别相关module模块问题的解决过程

    这篇文章主要给大家介绍了关于IDEA无法识别相关module模块问题的解决过程,文中通过图文介绍的非常详细,对大家学习或者使用IDEA具有一定的参考借鉴价值,需要的朋友可以参考下
    2023-07-07
  • Eclipse、MyEclipse 导入svn项目具体步骤

    Eclipse、MyEclipse 导入svn项目具体步骤

    这篇文章主要介绍了Eclipse、MyEclipse 导入svn项目具体步骤的相关资料,需要的朋友可以参考下
    2016-10-10
  • idea如何忽略.iml文件和.idea目录

    idea如何忽略.iml文件和.idea目录

    这篇文章主要介绍了idea如何忽略.iml文件和.idea目录问题,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2023-12-12

最新评论