HashSet通过hashCode()快速定位桶、再用equals()在桶内精确判断重复;二者必须同时重写且逻辑一致,否则因哈希值不同导致去重失效,如未重写时两个相同Person对象会被视为不同元素。

HashSet 的唯一性不单靠 hashCode() 判定,而是用它做“快速分流”,再配合 equals() 精确确认——hashCode() 本身不能决定是否重复,但它是去重流程的第一道门槛。
hashCode 决定“去哪个桶”
HashSet 底层用 HashMap 存储,元素作为 key。添加时先调用对象的 hashCode(),对哈希值取模得到数组索引(即“桶号”)。这步是 O(1) 的定位操作:
- 哈希值不同 → 一定进不同桶 → 不会触发 equals 比较 → 直接插入(即使内容相同,也会被当成不同元素)
- 哈希值相同 → 进入同一个桶 → 才开始用
equals()逐个比对
只靠 hashCode 无法保证唯一性
哈希冲突天然存在:不同对象可能算出相同哈希值(比如两个不同字符串碰巧 hashCode() 相同)。这时仅看哈希值会误判为“重复”,所以必须用 equals() 二次验证:
-
hashCode()相同 +equals()返回true→ 视为重复,添加失败 -
hashCode()相同 +equals()返回false→ 非重复,允许共存于同一桶中(链表或红黑树)
没重写 hashCode 就等于没去重
自定义类若未重写 hashCode(),默认继承 Object 的实现,返回内存地址哈希值。结果就是:
立即学习“Java免费学习笔记(深入)”;
-
new Person("Alice", 25)和new Person("Alice", 25)哈希值不同 → 分到不同桶 → 完全不比较equals()→ 两个都加进去了 - 哪怕你重写了
equals()让它们逻辑相等,也因哈希值不同而绕过判断
String 为什么能直接去重
因为 String 类早已重写了 hashCode() 和 equals(),且严格遵守契约:
- 内容相同的字符串 →
hashCode()一定相同 → 进同一桶 - 进桶后
equals()返回true→ 被识别为重复


















