重写 equals 和 hashcode 必须同时进行,且严格满足相等对象哈希码相同这一契约,否则会导致 hashset、hashmap 等集合查找失败、重复添加或无法删除;equals 需满足自反性、对称性、传递性、一致性及对 null 的处理;推荐用 getclass() 判断类型、objects.equals() 和 objects.hash() 实现,并避免使用可变字段;类若可被继承需谨慎设计,务必通过测试验证去重与存取正确性。

重写 equals 和 hashCode 必须同时进行,且必须满足“相等的对象必须有相同的哈希码”这一契约,否则在 HashSet、HashMap 等基于哈希的集合中会出现查找失败、重复添加、无法删除等问题。
equals 方法要满足五项基本约定
Java 规范要求 equals 必须是:
-
自反性:对任意非 null 对象 x,
x.equals(x)必须返回true -
对称性:若
x.equals(y)为 true,则y.equals(x)也必须为 true -
传递性:若
x.equals(y)和y.equals(z)都为 true,则x.equals(z)也必须为 true - 一致性:多次调用结果不变(只要参与比较的字段没变)
-
对 null 的处理:对任意非 null 对象 x,
x.equals(null)必须返回false
常见错误是忽略类型检查或使用 instanceof 在继承场景下破坏对称性(如父类和子类互比较)。稳妥做法是用 getClass() == obj.getClass() 严格判断运行时类型。
hashCode 方法必须与 equals 逻辑一致
只要两个对象通过 equals 判定为相等,它们的 hashCode 就必须相同。反之不成立(哈希码相同不一定相等)。
推荐做法是:只对 equals 中实际参与比较的字段计算哈希码。例如:
Java项目代码review工具。分析Git变更+完整调用链路上下文,推断业务需求,进行多维度评分和分类汇总,生成完整PRD文档。包含细粒度Java代码审查清单(Null安全、异常处理、Streams、并发、equals/hashCode、资源管理、API设计、性能、MyBatis/ORM、事务边界、SQL/DD...
public int hashCode() {
return Objects.hash(id, name, age); // id、name、age 是 equals 中用到的字段
}
避免使用可变字段(如未被 final 修饰且后续可能修改的属性),否则对象加入哈希集合后若字段变更,会导致哈希桶错位,再也找不到了。
使用 IDE 或工具生成更安全
手动写容易遗漏 null 检查或字段不一致。建议用 IntelliJ IDEA 或 Eclipse 自动生成,或使用 Objects.equals() 和 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 age == person.age &&
Objects.equals(id, person.id) &&
Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(id, name, age);
}
注意:如果类会被继承,且子类也需参与 equals 比较,应谨慎设计——通常建议将类设为 final,或明确文档说明子类必须重写并遵守契约。
测试关键场景不能少
光写完不验证等于白做。至少覆盖:
- 同一对象调用
equals返回 true - 不同但内容相同的对象,
equals为 true 且hashCode相同 - 内容不同的对象,
equals为 false(不必关心哈希码是否相同) - 放入
HashSet后能否正确去重;作为HashMap的 key 能否正常存取
例如:Set<person> set = new HashSet(); set.add(p1); set.add(p2);</person> 若 p1.equals(p2) 为 true,最终 set.size() 应为 1 —— 这是最直接的验证方式。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










