重写 equals() 必须同时重写 hashcode(),否则逻辑相等的对象哈希值不同,导致 hashmap 存取失败;二者须基于相同不可变字段,推荐用 objects.equals() 和 objects.hash() 并严格校验字段一致性。

为什么重写 equals() 必须同时重写 hashCode()
不重写 hashCode() 会导致同一个逻辑相等的对象,算出的哈希值不同,从而被放进 HashMap 的不同桶里。查的时候用 get() 找不到——不是没存,是存错了地方。
Java 规范明确要求:equals() 返回 true 的两个对象,hashCode() 必须返回相同整数。违反这条,HashMap 就不可靠。
equals() 和 hashCode() 要基于哪些字段?
只选参与逻辑相等判断的字段。比如 User 类中,如果业务上认为 id 唯一决定对象相等性,那就只用 id;如果还要看 name 和 email,那三者都得进 equals() 和 hashCode()。
常见错误:
- 在
equals()里用了name,但hashCode()只用了id - 用了可变字段(如
status),对象存进HashMap后改了它,再get()就失效 - 字段为
null时没做空判断,equals()抛NullPointerException
推荐写法:用 Objects.equals(a, b) 和 Objects.hash(f1, f2, ...),自动处理 null 安全。
一个安全、可直接抄的模板
假设 Person 类用 id 和 name 判等:
public class Person {
private final Long id;
private final String name;
// 构造、getter 略
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return Objects.equals(id, person.id) &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name);
}
}
注意点:
-
getClass() != o.getClass()比instanceof更严格,避免子类误判相等 - 字段加
final是加分项——防止中途修改导致哈希值漂移 - 别在
hashCode()里调用可能抛异常或计算昂贵的方法
测试是否真能正常存取
光看编译通过没用,得验证行为:
- 存一个对象,再用另一个
equals()为true的对象去get(),必须命中 - 两个对象
equals()为true,但hashCode()不同 → 这是 bug,立刻修 - 改完字段后立即
get(),仍能命中 → 说明没用可变字段
最简验证代码:
Person p1 = new Person(1L, "Alice"); Person p2 = new Person(1L, "Alice"); Map<person string> map = new HashMap(); map.put(p1, "data"); System.out.println(map.get(p2)); // 必须输出 "data"</person>
如果输出 null,要么 equals() 没生效,要么 hashCode() 算错了——优先检查 Objects.hash() 的参数和 equals() 的字段是否完全一致。
最容易被忽略的是:字段顺序不影响 equals(),但 Objects.hash(f1, f2) 和 Objects.hash(f2, f1) 结果相同,所以顺序其实不关键;真正关键的是「参与比较的字段集合」必须一字不差地同步出现在两个方法里。










