hashset.add()能自动去重的关键在于底层hashmap的put()机制,它依赖hashcode()和equals()共同判断重复;若只重写equals()不重写hashcode(),会导致逻辑相等的对象被当作不同元素添加。

HashSet.add() 为什么能自动去重
关键不在 HashSet 本身,而在它底层用的 HashMap:每次 add() 实际是往 HashMap 里插一个键值对,键是元素,值是固定的 PRESSENT 对象。重复与否,完全由 HashMap 的 put() 决定——而 put() 判断键是否已存在,只看两件事:hashCode() 是否相同,且 equals() 是否返回 true。
所以不是 HashSet “有去重逻辑”,而是你传进去的对象,必须自己守好 hashCode() 和 equals() 的契约,否则它就“认不出”重复。
重写 equals() 却没重写 hashCode() 的后果
这是最常踩的坑:对象明明 equals() 返回 true,却能被 HashSet 当成两个不同元素加进去。
- 现象:
new Person("Alice", 25)和new Person("Alice", 25)被add()两次,size()变成 2 - 原因:默认
hashCode()返回内存地址,两个对象地址不同 → 被分到HashMap不同桶里 → 根本不调equals() - 修复:只要重写了
equals(),就必须重写hashCode(),且保证“相等的对象必须有相同哈希码”
hashCode() 返回常量(比如 1)会怎样
语法上合法,但性能灾难:
立即学习“Java免费学习笔记(深入)”;
- 所有对象都进同一个桶,
HashMap退化成链表(Java 8+ 超过阈值转红黑树,但仍是 O(log n) 而非 O(1)) -
add()、contains()、remove()全部变慢,集合越大越明显 - 正确做法:用参与
equals()比较的字段来算哈希,比如Objects.hash(name, age)
自定义类放进 HashSet 前必须检查的三件事
缺一不可,漏掉任意一条都会导致行为异常:
-
equals()方法是否被重写?且逻辑是否覆盖了业务上“相等”的全部条件 -
hashCode()是否同步重写?且计算是否只依赖equals()中用到的字段 - 字段是否可变?如果对象加进
HashSet后修改了影响hashCode()或equals()的字段,这个对象很可能再也找不到了(因为桶位置变了,但没重新散列)
最后一句:别指望 IDE 自动生成的 hashCode() 和 equals() 就万事大吉——得亲手验证它们在你的数据场景下真能协同工作。










