必须重写hashcode(),因为java规范要求equals()为true的对象hashcode()必须相同,否则hashmap、hashset等集合会出现去重失效、get()返回null等运行时异常;核心是参与equals()比较的不可变字段也必须参与hashcode()计算,推荐使用objects.hash()。

重写 equals 和 hashCode 不是可选项,而是契约性要求:只要重写了 equals,就必须重写 hashCode,否则在 HashMap、HashSet 等集合中会出现逻辑错误——比如对象明明相等却查不到、重复插入、去重失效。
equals 方法的正确写法
核心是遵守五个约定:自反性、对称性、传递性、一致性、非空性。实际编码按顺序做四件事:
- 先用
this == obj判断是否为同一引用(性能快,直接返回true) - 再检查
obj == null或类型不匹配:推荐用getClass() != obj.getClass()(比instanceof更安全,避免子类破坏对称性) - 强制转换后,用
Objects.equals(a, b)比较每个关键字段(自动处理null,不抛NullPointerException) - 所有用于判断相等的字段,必须和
hashCode中使用的字段完全一致
hashCode 方法的正确写法
目标是让 equals 返回 true 的对象,一定有相同的哈希值。最稳妥的做法是:
- 只基于
equals中参与比较的**不可变字段**计算(避免对象入集合后修改字段导致哈希码变化、查找失败) - 使用
Objects.hash(field1, field2, ...)—— 它内部已做null安全处理,且生成的哈希分布较均匀 - 不要手写
31 * a + b这类公式,除非有特殊性能需求;Objects.hash足够高效且不易出错
常见错误要避开
这些看似微小的疏忽,常导致线上集合行为异常:
- 只重写
equals,不重写hashCode→HashSet存两个“相等”对象,HashMap查不到已存的键 - 在
hashCode中用了可变字段(如后期会setAge()的属性)→ 对象存进HashSet后改字段,再也无法被remove或contains - 用
instanceof做类型检查 → 当存在继承关系时(如Student extends Person),可能破坏对称性:s.equals(p)为true,但p.equals(s)却为false - 比较字段时直接调用
a.equals(b)而不判空 → 若某个字段为null,运行时报NullPointerException
推荐用 Lombok 简化(生产环境可用)
如果项目允许引入 Lombok,加两个注解即可自动生成符合规范的实现:
@EqualsAndHashCode(of = {"id", "name"})
public class Person {
private String id;
private String name;
private int age; // age 不参与 equals/hashCode
}
它默认使用 getClass() 判型、用 Objects.equals 和 Objects.hash,且跳过 transient 和静态字段。手动写适合学习原理,Lombok 适合工程提效与减少低级错误。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











