hashmap put方法动态选择链表或红黑树:链表长度≥8且数组长度≥64才树化,节点≤6则退化为链表;需通过固定哈希key和反射验证类型转换。

掌握 HashMap 的 put 方法实战,关键不是背代码,而是理解它在不同数据规模下如何动态选择链表或红黑树,并在临界点触发转换。这种“自动适配”逻辑直接影响性能表现,尤其在高并发、大数据量场景中。
链表插入与长度监控:从第一个冲突开始
每次 put 遇到哈希冲突(即桶位置非空),HashMap 会遍历当前链表:
- 逐个比对
hash和equals,找到相同 key 就更新 value - 没找到就尾插新节点,并用局部变量
binCount实时统计当前链表已有节点数(含头节点) - 当
binCount == 7(注意:是>= TREEIFY_THRESHOLD - 1,因为阈值默认为 8),说明链表已有 8 个节点,满足树化前置条件之一
树化触发的双重门槛:不能只看链表长度
链表长度 ≥ 8 只是必要条件,不是充分条件。真正执行 treeifyBin 还需同时满足:
- 数组总长度
tab.length >= MIN_TREEIFY_CAPACITY(默认 64) - 若数组长度不足 64(比如刚初始化或经历多次扩容失败),
put会优先调用resize()扩容,而不是树化 - 只有扩容后仍冲突严重(链表再达 8),且数组已 ≥ 64,才会把链表节点整体转为
TreeNode并构建红黑树
红黑树插入与反向退化:不是一劳永逸
一旦转成红黑树,后续 put 会走 putTreeVal 流程,按二叉搜索树规则插入并自动平衡:
- 查找、插入、删除都保持
O(log n)时间复杂度 - 但红黑树有空间开销(每个节点多存颜色、父/子引用等字段)
- 当执行
remove或resize导致树中节点数 ≤ 6(UNTREEIFY_THRESHOLD),整个树会退化回普通链表,节省内存
实战验证建议:用小数据观察行为边界
写一段可复现的测试代码,能快速验证转换逻辑:
- 构造一个固定哈希值的 Key 类(重写
hashCode始终返回相同值),强制所有 put 落入同一桶 - 先
new HashMap(16),然后连续 put 64 个不同 key → 观察第 65 次是否触发树化(此时数组已 ≥ 64,链表长度将达 8) - 再 remove 其中 59 个 → 剩余 6 个节点,下次 put 不会立即树化;再删 1 个只剩 5 个,此时即使再 put 新 key,也不会触发树化
- 用反射或调试器检查
table[i]的实际类型(Node还是TreeNode),比单纯看性能更直观










