红黑树退化为链表仅发生在扩容resize过程中,当某桶迁移后节点数≤6时调用untreeify()转链表;删除、插入、clear等操作均不触发退化,设计上为避免频繁结构切换带来的性能开销。

红黑树退化为链表,只发生在扩容过程中,不是响应式操作,也没有其他触发路径。
退化只在 resize 时发生
HashMap 的红黑树不会因为删除、插入、clear 或手动干预而退化。唯一可能退化的时机,是执行 resize()(扩容)时,对某个树桶调用 split() 方法迁移节点。迁移完成后,若该桶中剩余的 TreeNode 节点数 ≤ 6,就会调用 untreeify() 转为普通链表。
- 扩容前是红黑树,不代表扩容后还是;是否退化取决于迁移后的实际节点数
- 哪怕 remove() 删到只剩 1 个节点,树结构仍保留,不退化
- clear() 是清空所有桶引用,不是“退化”,也不检查节点数量
退化阈值固定为 6,不可配置
源码中 UNTREEIFY_THRESHOLD = 6 是硬编码常量,和树化阈值 8 构成不对称设计:
Java开发手册规约集合,基于阿里巴巴Java开发手册(嵩山版)。 涵盖7大维度:编程规约、异常日志、单元测试、安全规约、MySQL数据库、工程结构、设计规约。
- 树化门槛高(≥8),避免小规模冲突就建树
- 退化门槛低(≤6),防止节点数在 7 和 8 之间反复横跳,引发结构震荡
- 这个差值是刻意留出的缓冲区,减少旋转、着色、节点转换等开销
为什么不在删除时检查退化?
设计上主动规避运行时频繁结构切换:
- 如果每次 remove 都检查节点数,可能刚删一个就退化,再 put 一个又触发树化,带来额外成本
- 红黑树的维护本身有代价:着色、旋转、节点类型转换(TreeNode → Node)都不轻量
- 扩容是天然的批量重组织时机,此时统一判断更高效、更可控
ConcurrentHashMap 的行为更严格
它同样只在并发扩容(transfer)中退化,但要求更高:
- 必须已对当前桶加锁,且迁移完成
- 节点数 ≤ 6 才会 untreeify
- 日常 remove 操作完全不触发退化,比 HashMap 更难退化
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










