为什么要重写 hashCode 和 equals 方法?¶
结论¶
不重写会违反"相等的对象必须有相等的哈希码"约定,导致在 HashMap、HashSet、HashTable 中行为异常。
约定(Java 官方规范)¶
- 一致性:同一个对象多次调用
hashCode,必须返回同一个 int。 - 相等则哈希相等:
a.equals(b) == true,则a.hashCode() == b.hashCode()。 - 不相等则哈希可以相等(哈希碰撞允许),但尽量不相等以提高散列性能。
不重写会出什么问题¶
Object 默认的 equals 是 ==(比较地址),hashCode 是对象内存地址派生的 int。这意味着:
class Person {
String name;
Person(String name) { this.name = name; }
}
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
HashMap<Person, Integer> map = new HashMap<>();
map.put(p1, 1);
map.get(p2); // 返回 null!p1 和 p2 是两个对象,hashCode 不同
p1 和 p2 逻辑上是同一个人,但因为没重写 hashCode,HashMap 把它们分到了不同桶。
正确做法¶
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person p = (Person) o;
return Objects.equals(name, p.name);
}
@Override
public int hashCode() {
return Objects.hash(name);
}
工程实践
- IDE(IDEA/Eclipse)可一键生成。
- Lombok 的
@Data/@EqualsAndHashCode自动生成。 - 重写时必须同时重写两者,不能只重写一个。
- 用作 HashMap key 的对象,equals 用到的字段必须是不可变的,否则 put 后修改字段会导致 get 不到。