必须同时重写equals和hashcode,否则hashset去重和查找失效;因哈希桶定位依赖hashcode,桶内比对依赖equals,二者缺一不可且逻辑必须一致。

向 Set(尤其是 HashSet)添加自定义对象时,必须同时重写 equals 和 hashCode,这不是“建议”,而是底层哈希机制运行的硬性契约——违反它,Set 就会失效:该去重的没去重,该查到的查不到。
HashSet 底层靠哈希桶 + 链式比对协同工作
HashSet 实际是用 HashMap 实现的,只存 key。添加一个对象时,流程严格分两步:
- 先调用对象的
hashCode(),算出一个整数,决定它该放进哪个“桶”(数组索引位置); - 再在那个桶里遍历已有元素,逐个调用
equals()判断是否逻辑相等。
这两个步骤缺一不可。只靠 hashCode 无法确认相等(哈希冲突天然存在),只靠 equals 又太慢(得全量扫描)。
不重写或只重写一个,Set 就会“失智”
假设你有一个 User 类,两个对象内容完全一样(name="张三", id=1001),但没重写方法:
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 用默认
hashCode(基于内存地址),它们哈希值大概率不同 → 被分到不同桶 →equals根本不会被调用 → Set 认为这是两个不同对象,重复插入; - 如果只重写了
equals(按业务字段比较返回 true),但hashCode没改 → 哈希值仍不同 → 还是进不同桶 → 同样导致重复; - 如果只重写了
hashCode(比如固定返回 1),但equals没改(仍用 == 比较)→ 所有对象哈希值相同,全挤在一个桶里 →contains或remove退化成链表遍历,性能暴跌,且依然判不出相等。
重写的逻辑必须保持一致
equals 和 hashCode 的契约核心就一条:只要 equals 返回 true,hashCode 就必须返回相同值。反过来不要求(不同对象可以同哈希),但尽量让不同对象哈希值也不同,能提升性能。
实操建议:
- 参与
equals比较的字段,必须全部参与hashCode计算(如用Objects.hash(name, id)); - 不参与业务判等的字段(如
createTime、password),两个方法里都排除; - 推荐用 Lombok 的
@EqualsAndHashCode注解,自动按指定字段生成,避免手写遗漏或不一致。
其他 Set 实现要不要重写?
要看具体实现:
-
TreeSet不依赖hashCode和equals,它用compareTo或Comparator排序和判等,所以不用重写那俩方法(但要确保排序逻辑与业务相等一致); -
LinkedHashSet是 HashSet 的子类,底层仍是哈希表,同样强制要求重写; - 只要底层基于哈希(HashMap/HashTable),就必须守这个契约。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










