
要理解这个改动,我们需要先回顾一下HashMap中为什么需要引入红黑树。
在JDK 8之前,HashMap的每个桶(bucket)完全由链表实现。虽然链表在一般情况下表现良好,但如果发生大量的哈希碰撞(即多个key的hash值都映射到了同一个桶),链表会变得非常长。此时,查询、插入操作的时间复杂度会从理想的O(1)退化为O(n),严重影响性能。
为了解决这个问题,JDK 8引入了重大改进:当链表长度超过一定阈值时,会将其转换为红黑树。红黑树是一种自平衡的二叉搜索树,其查询、插入、删除的最坏时间复杂度都能保持在O(log n)。这样,即使在最坏的情况下,性能也不会退化得太严重。
在JDK 8到JDK 16中,这个树化(treeify)的阈值是 8。同时,还有一个与之配对的解除树化(untreeify) 的阈值 6。
为什么要设置两个不同的阈值? 这是为了避免在临界值附近发生频繁的、昂贵的结构转换。想象一下,如果阈值都是7,那么当一个桶的元素数量在7附近频繁增减时,HashMap会不断地在链表和红黑树之间来回转换,这个转换过程本身就有性能开销(创建树节点、调整平衡等)。设置一个2的差值(8和6),提供了一个“缓冲带”,防止了这种抖动。
现在,核心问题来了:既然8和6的缓冲带设计得很好,为什么JDK 17还要把树化阈值从8降到6呢?
根本原因:为了更早地预防性能退化,尤其是在哈希碰撞被恶意构造的攻击场景下。
JDK 17将链表转红黑树的阈值从8调整为6,是一项经过深思熟虑的优化:
树化 > 6,解除树化 <= 6),避免了在临界点频繁转换。这个改动体现了Java持续优化其核心库,尤其是在安全性和极端情况性能方面的重视。对于普通开发者来说,这个改动是完全透明的,你不需要修改任何代码,就能享受到它带来的潜在性能和安全性的提升。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。