必须同时重写equals和hashCode,否则HashMap、HashSet等哈希表结构会失效;因查找时先用hashCode定位桶,再用equals比对,若相等对象hashCode不同,则无法找到或误判重复。

重写 hashCode 必须同时重写 equals
不这么做,哈希表(如 HashMap、HashSet)会直接失效。因为 HashMap 查找时先用 hashCode 定位桶,再用 equals 比较键是否真正相等。如果两个逻辑上相等的对象(equals 返回 true)返回不同 hashCode,它们会被散列到不同桶里,导致 get 找不到、contains 返回 false——哪怕对象明明在集合中。
常见错误现象:
-
map.put(new Person("Alice", 30), "data");后,map.get(new Person("Alice", 30))返回null -
set.add(p1); set.contains(p2)返回false,即使p1.equals(p2) == true
怎么生成靠谱的 hashCode 值
核心原则:相等的对象必须有相同哈希码;不等的对象尽量有不同哈希码(减少冲突)。别手写质数乘法,用 JDK 自带工具更安全。
- JDK 7+ 推荐用
Objects.hash(...):传入所有参与equals判断的字段,自动处理null和组合计算 - 字段顺序必须和
equals中的比较顺序一致(否则可能违反对称性) - 避免使用可变字段(如
ArrayList内容变化后hashCode改变),否则对象放入哈希表后行为不可预测 - 不要用随机数、当前时间、内存地址等不稳定值
示例:
public class Person {
private final String name;
private final int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age); // ✅ 顺序与 equals 一致,且用 final 字段
}
}
IDE 自动生成的 hashCode 可靠吗
IntelliJ / Eclipse 生成的版本基本可用,但要注意几个坑:
- 默认可能包含
transient字段或非业务字段(如日志器、上下文对象),要手动删掉 - 如果用了 Lombok,
@Data会自动生成,但若类里有自定义equals却没配@EqualsAndHashCode,Lombok 仍按字段生成,容易不一致 - 某些旧版 IDE 生成器对
float/double用Float.floatToIntBits不够严谨,建议统一走Objects.hash
重写后还要注意什么
哈希码一致性只是一半,真正让哈希表“存取一致”的关键是对象状态稳定:
- 把参与
hashCode计算的字段设为final最稳妥;若必须可变,确保修改后不放入哈希容器,或修改前先remove再put - 子类继承时,如果重写了
equals或hashCode,父类实现不能破坏契约(比如父类hashCode只算 id,子类加了 name,但没重写equals的逻辑) - 序列化/反序列化后对象的
hashCode应该和原对象一致(Objects.hash满足这点)
最常被忽略的一点:改完 hashCode 和 equals 后,没清空已存在的集合缓存,导致旧数据行为异常——不是代码写错了,是运行时状态没同步。










