equals相等时hashCode必须相等,这是Java强制契约;否则HashMap、HashSet等集合行为异常,如重复添加或查找失败;修复需同步重写二者,且字段一致,推荐用Objects.hash()。

equals相等时,hashCode必须相等——这是硬性契约
Java规定:如果 a.equals(b) == true,那么 a.hashCode() == b.hashCode() 必须成立。这不是建议,而是JVM集合类(如 HashMap、HashSet)正常工作的前提。
- 违反它,
HashSet.add(obj)可能重复添加逻辑上相同的对象;HashMap.get(key)可能查不到已存的键 - 典型现象:
new Product(100, "iPhone")和另一个相同字段的Product实例,放进HashSet后size()为2,但contains()却返回false - 原因:集合先用
hashCode()定位桶(bucket),若两个对象哈希值不同,压根不会调用equals()去比——它们被扔进完全不同的位置了
只重写equals不重写hashCode?等于给HashMap埋雷
很多初学者在自定义类里重写了 equals() 判断业务字段(比如 id 和 name),却忘了同步更新 hashCode(),结果就是集合行为失常。
- Object 默认的
hashCode()基于内存地址,两个内容相同但不同实例的对象,哈希值必然不同 - String、Integer 等类之所以能安全放进
HashSet,正是因为它们**同时重写了两个方法** - 修复方式不是手算哈希:优先用
Objects.hash(id, name),它自动处理 null、组合字段、散列分布
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Product product = (Product) o;
return id == product.id && Objects.equals(name, product.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name); // ✅ 关键:字段必须和equals中参与比较的一致
}
hashCode相等 ≠ 对象相等——哈希冲突是常态
hashCode() 返回的是 32 位整数,而现实世界中的对象数量远超 2³²,所以不同对象算出相同哈希值(即哈希冲突)完全正常,也不影响正确性。
- 例如:
"Aa".hashCode() == "BB".hashCode()都是 2112 —— 这不是 bug,是数学必然 - HashMap 的设计早已应对:哈希值相同 → 落入同一桶 → 再用
equals()逐个比对 - 真正要警惕的是“大量冲突”:如果所有对象哈希值都集中在一个数(比如全返回 1),性能会退化成 O(n),这时才需检查
hashCode()实现是否太弱(比如只用一个常量字段)
哪些场景必须自己动手写equals和hashCode?
不是每个类都需要重写。只有当你的对象要作为 HashMap 的键、HashSet 的元素,或被用于依赖逻辑相等判断的框架(如 Hibernate 实体、JUnit 断言)时,才需要动手。
立即学习“Java免费学习笔记(深入)”;
- DTO/VO 类通常不用——它们只是数据搬运工,不参与集合去重或查找
- 实体类(如
User、Order)几乎一定需要:主键或业务唯一标识字段必须纳入equals和hashCode - IDE 自动生成(IntelliJ 的
Alt+Insert)可用,但务必核对:是否漏掉关键字段?是否误加了可变字段(如lastModifiedTime)?
最易被忽略的一点:如果类中用了 BigDecimal 或数组,别直接调用 .hashCode()——BigDecimal 的 equals() 和 hashCode() 是基于数值而非精度,而数组默认的 hashCode() 是内存地址,要用 Arrays.hashCode() 替代。










