Java8中HashSet发生哈希冲突时首先调用什么方法比较对象?
结论
版本2的描述符合Java 8中HashSet的实际实现逻辑,版本1的流传说法是错误的。
实际插入冲突处理流程
HashSet本身没有独立实现集合操作逻辑,所有add()操作全部委托给内部持有的HashMap实例完成,核心判断逻辑对应HashMap的putVal方法,发生槽位占用时的完整处理流程如下:
- 插入新元素时,首先调用元素的
hashCode()获取原始哈希值,经过固定扰动计算后得到最终用于定位的哈希值,按这个值找到底层数组对应的存储槽位 - 如果目标槽位为空,直接存入新元素,插入流程结束
- 如果目标槽位已经存在元素,首先做成本极低的整数相等判断:比较槽位上已有元素和新元素的扰动后哈希值
- 若哈希值不相同,直接判定两个元素不重复,将新元素挂载到对应槽位的链表尾部(链表长度达到8且数组长度达标时会转红黑树,此时插入红黑树对应位置),整个过程不会调用
equals()方法 - 若哈希值相同,才会进一步调用
equals()方法判断两个对象是否实际相等:equals()返回true,判定为重复元素,拒绝插入新元素(底层实际是覆盖HashMap对应key的value,而HashSet所有key映射的value都是固定静态常量PRESENT,对外表现就是插入失败)equals()返回false,判定为不重复,继续遍历当前槽位挂载的链表/红黑树,找到空位完成插入
- 若哈希值不相同,直接判定两个元素不重复,将新元素挂载到对应槽位的链表尾部(链表长度达到8且数组长度达标时会转红黑树,此时插入红黑树对应位置),整个过程不会调用
版本1的常见误区
版本1的说法之所以流传广,是因为很多人混淆了“槽位冲突”和“哈希值相等”的概念:不同哈希值的元素完全可能因为位运算定位规则落到同一个数组槽位,这类场景下根本不需要调用equals()做判断。JDK特意设计了哈希值前置比对的逻辑,就是因为整数比较的性能远高于绝大多数业务场景自定义的equals()实现,可以过滤掉绝大多数非重复场景,大幅减少不必要的方法调用,降低插入开销。
内容的提问来源于stack exchange,提问作者Yzc0922
相关产品推荐
相关产品推荐

