重写equals时必须同步重写hashCode,否则破坏哈希集合正确性;二者须基于完全相同的字段计算,遵守相等对象hashCode必须相等的契约,推荐使用Objects.hash确保安全与一致。

重写 equals 时必须同步重写 hashCode,否则会破坏哈希集合(如 HashMap、HashSet)的正确性——相同对象可能被散列到不同桶中,导致 contains 返回 false 或重复添加。
核心契约必须遵守
Java 规定:如果两个对象通过 equals 判定为相等,则它们的 hashCode 必须返回相同整数。反之不强制(不同对象可有相同 hash),但应尽量减少冲突。
- 相等的对象 →
hashCode()值必须相等 - 不相等的对象 →
hashCode()值可以相同(允许哈希碰撞),但设计上应尽量分散 -
hashCode()在对象生命周期内,只要影响equals的字段没变,返回值就不能变
重写步骤要一致
确保 equals 和 hashCode 基于**完全相同的字段**计算:
- 先列出所有参与
equals比较的非静态字段(通常是业务关键属性) - 在
hashCode中用这些字段参与计算,顺序和逻辑保持一致 - 忽略只读常量、临时计算字段、或未参与
equals判断的字段
例如:若 equals 只比较 id 和 name,那 hashCode 就不能加入 createdAt 或 status。
立即学习“Java免费学习笔记(深入)”;
推荐写法:用 Objects.hash
避免手写易错的质数乘法,直接用 java.util.Objects.hash(...):
@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);
}
<p>@Override
public int hashCode() {
return Objects.hash(id, name); // 自动处理 null,且与 equals 字段严格对应
}它内部使用 31 * h + Objects.hashCode(field),兼顾效率与分布质量,且对 null 安全。
常见错误要避开
以下写法看似合理,实则违反契约:
- 在
hashCode中漏掉某个equals用到的字段(比如只算id,但equals还比了name) - 在
hashCode中用了可变字段(如cacheValue),而该字段变化后equals结果不变,却导致 hash 改变 - 重写了
equals却忘记重写hashCode(编译器不报错,但运行时出问题) - 用随机数、时间戳、或
System.identityHashCode等不稳定值作为 hash


















