2024-03-23  阅读(3)
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://www.skjava.com/mianshi/baodian/detail/1471739948

ConcurrentHashMap 的底层结果如下:

学过 ConcurrentHashMap 小伙伴都知道,ConcurrentHashMap 开始时是链表结构,当链表长度为 8 时,就开始由链表转换为红黑树。

这里我们要回答两个问题:

  • 1、为什么要转为红黑树

主要原因还是性能。

我们知道链表的查询元素时间复杂度为 O(n),这就意味着随着链表长度的增加,查找效率会逐步降低。而红黑树是一种平衡二叉树,时间复杂度为 O(log n),查询效率明显高于链表。

  • 2、为什么是 8 的时候转换为红黑树

ConcurrentHashMap 中,要满足两个条件才会由链表转换为红黑树:

  • ConcurrentHashMap 总结点数大于 64

这是为了避免在哈希表较小的情况下就进行转换,当哈希表较小的时候,链表的查询效率通常是可以接受的。同时红黑树中的 TreeNode 是链表中的 Node 所占空间的 2 倍,如果过早地转换会浪费空间。同时,红黑树的数据结构相比链表要复杂得多,维护成本较高。

  • ConcurrentHashMap 一个桶(bucket)中的节点数量大于 8

当链表长度达到 8 时,链表会转换为红黑树,而当其降为 6 时,红黑树又会转换回去。这体现的是时间和空间平衡的思想。

刚刚开始使用链表是因为链表占用空间小,数据量少,所以查询效率也没太大问题。但是当链表越来越长时,就需要用红黑树的形式来保证查询的效率。那什么时候转换为红黑树呢?这个阈值的确认是基于经验和实验。

源码中有这样一段描述:

Because TreeNodes are about twice the size of regular nodes, we use them only when bins contain enough nodes to warrant use (see TREEIFY_THRESHOLD). And when they become too small (due to removal or resizing) they are converted back to plain bins. In usages with well-distributed user hashCodes, tree bins are rarely used. Ideally, under random hashCodes, the frequency of nodes in bins follows a Poisson distribution (http://en.wikipedia.org/wiki/Poisson_distribution) with a parameter of about 0.5 on average for the default resizing threshold of 0.75, although with a large variance because of resizing granularity. Ignoring variance, the expected occurrences of list size k are (exp(-pow(0.5, k) / factorial(k)). The first values are:
 
0: 0.60653066
1: 0.30326533
2: 0.07581633
3: 0.01263606
4: 0.00157952
5: 0.00015795
6: 0.00001316
7: 0.00000094
8: 0.00000006
more: less than 1 in ten million

这段话的大致意思是:当 hashCode 离散性很好的时候,红黑树使用的概率会非常小,因为各个值都均匀分布,很少会出现链表很长的情况,在理想情况下,桶中节点数概率(链表长度)符合泊松分布(如上),只有0.00000006 的概率才会出现桶中节点数节点为 8 的情况。所以,在理想情况下,我们一般都不会用到红黑树。

但是,每个人的算法水平参差不齐,无法保证实现的 Hash 算法就是理想的,如果出现数据分布不均匀的情况,就可以利用红黑树来提升查询性能。

所以,链表长度超过 8 就转换为红黑树的设计,更多的一种防御措施,防止用户实现了不好的 Hash 算法,导致链表过长而使查询效率低下。所以,在平时开发过程中,如果遇到 HashMap 或是 ConcurrentHashMap内部出现了红黑树的结构,有可能是你 hash 算法不够优秀,需要进行优化!


Java 面试宝典是大明哥全力打造的 Java 精品面试题,它是一份靠谱、强大、详细、经典的 Java 后端面试宝典。它不仅仅只是一道道面试题,而是一套完整的 Java 知识体系,一套你 Java 知识点的扫盲贴。

它的内容包括:

  • 大厂真题:Java 面试宝典里面的题目都是最近几年的高频的大厂面试真题。
  • 原创内容:Java 面试宝典内容全部都是大明哥原创,内容全面且通俗易懂,回答部分可以直接作为面试回答内容。
  • 持续更新:一次购买,永久有效。大明哥会持续更新 3+ 年,累计更新 1000+,宝典会不断迭代更新,保证最新、最全面。
  • 覆盖全面:本宝典累计更新 1000+,从 Java 入门到 Java 架构的高频面试题,实现 360° 全覆盖。
  • 不止面试:内容包含面试题解析、内容详解、知识扩展,它不仅仅只是一份面试题,更是一套完整的 Java 知识体系。
  • 宝典详情:https://www.yuque.com/chenssy/sike-java/xvlo920axlp7sf4k
  • 宝典总览:https://www.yuque.com/chenssy/sike-java/yogsehzntzgp4ly1
  • 宝典进展:https://www.yuque.com/chenssy/sike-java/en9ned7loo47z5aw

目前 Java 面试宝典累计更新 400+ 道,总字数 42w+。大明哥还在持续更新中,下图是大明哥在 2024-12 月份的更新情况:

想了解详情的小伙伴,扫描下面二维码加大明哥微信【daming091】咨询

同时,大明哥也整理一套目前市面最常见的热点面试题。微信搜[大明哥聊 Java]或扫描下方二维码关注大明哥的原创公众号[大明哥聊 Java] ,回复【面试题】 即可免费领取。

阅读全文