阈值8源于泊松分布建模,桶中元素≥8概率低于千万分之一,表明哈希严重不均;需数组长度≥64才转树,避免小容量下误判;退化阈值设为6而非7,防止7-8间频繁抖动。

Java 中 HashMap 的链表转红黑树阈值不是拍脑袋定的,而是兼顾概率、性能和工程开销后权衡出来的结果。核心就两点:**为什么是 8?为什么还要加数组长度 ≥64 这个条件?**
链表长度 ≥8 才考虑转红黑树
这个数字来自泊松分布的统计建模。在理想哈希函数下,桶中元素数量服从泊松分布,λ(平均每个桶的元素数)≈ 负载因子(0.75)。算下来,一个桶里有 8 个或更多元素的概率低于千万分之一。换句话说,出现长度 ≥8 的链表,大概率不是偶然,而是哈希分布严重不均——比如 key 的 hashCode() 实现有问题,或者数据本身存在大量相似键。
再看性能拐点:链表查找平均要比较 n/2 次,红黑树是 log₂n。当 n=8 时,8/2 = 4,log₂8 = 3;n=7 时,7/2 = 3.5,log₂7 ≈ 2.8 —— 差距还不明显;但到 8,红黑树的理论优势开始稳定显现。继续用链表,最坏 O(n) 就真成了瓶颈。
数组长度必须 ≥64 才允许转换
这个限制是为了避免“小题大做”。如果当前数组才 16 或 32 个桶,却已经有某个桶链表长度到了 8,更可能的原因是还没来得及扩容,而不是哈希真的差。此时强行转红黑树,维护开销(旋转、着色、节点对象创建)反而得不偿失。
立即学习“Java免费学习笔记(深入)”;
而容量 ≥64 意味着: - 已经历至少两轮扩容(16→32→64),哈希分布本应趋于均匀 - 此时还出现长链表,说明问题出在 key 的 hash 算法或数据特征上,值得用树结构优化 - 同时,64 是 2 的幂,保证位运算寻址高效,也方便后续扩容时树节点的拆分
退化回链表的阈值设为 6 而不是 7
这是为了防止“抖动”:假如树化阈值是 8、退化阈值也是 7,那么在增删操作频繁、节点数在 7–8 之间来回波动时,就会反复树化又退化,白白消耗 CPU。
设成 6 就留出了缓冲区间: - 链表变长到 8 → 转树 - 树删到只剩 6 个节点 → 退链表 - 中间 7 这个状态不会触发任何转换,系统更稳
实际转换发生在 put 过程中,且是单向检查
转换动作不是定时扫描,而是在每次 put() 往某个桶插入新节点后立即判断:
- 先确认该桶当前是链表结构(不是树)
- 遍历链表,统计节点数(包括待插入的新节点)
- 同时检查全局 table.length ≥ 64
- 两个条件都满足,才触发 treeifyBin(),把整个链表重构为 TreeNode 树
注意:TreeNode 是 Node 的子类,内部多了 parent/left/right/red 字段,转换不是简单类型替换,而是节点重建+结构重排。


















