必须同时重写equals()和hashCode()以确保内容相等判断正确,否则HashMap、HashSet等集合会失效;equals()需遵守五条规范并按顺序处理实例、空值、类型、字段比较;hashCode()须仅基于equals()用到的字段计算,且保持稳定。

要让对象按“内容”而不是“地址”来判断相等,必须重写 equals() 和 hashCode(),且二者必须协同一致。只改 equals() 不改 hashCode() 会导致在 HashMap、HashSet 等集合中逻辑失效——比如两个内容相同的对象无法去重,或查不到。
重写 equals() 的关键步骤
目的是让比较基于字段值,而非内存引用。需严格遵守 Java 规范的五条约定(自反、对称、传递、一致、非空),实操中建议按顺序处理:
- 先用
this == obj判断是否为同一实例,是则直接返回true - 再判空:
obj == null→ 返回false - 检查类型:
getClass() != obj.getClass()→ 返回false(不建议用instanceof,避免子类打破对称性) - 强转后逐字段比较:基本类型直接比,引用类型用
Objects.equals(a, b)(自动处理 null),浮点数用Double.compare(a, b) == 0,数组用Arrays.equals(arr1, arr2)
重写 hashCode() 的核心原则
hashCode() 不是独立存在的,它只为支撑 equals() 在哈希集合中的正确行为。只要 equals() 中参与比较的字段变了,hashCode() 就必须重新计算;反之,这些字段不变,hashCode() 就不能变。
- 只基于
equals()中实际用到的字段计算哈希值,多一个或少一个都会破坏一致性 - 优先使用
Objects.hash(field1, field2, ...),它内部已处理 null 安全和散列组合逻辑 - 不要手写复杂算法,IDE(如 IntelliJ)的“Generate → hashCode and equals”功能生成的代码足够可靠
- 避免在
hashCode()中调用可能改变状态的方法或访问可变字段
为什么二者必须同时重写
哈希集合(如 HashSet)的查找流程是两步走:
- 第一步:调用
hashCode()快速定位桶(bucket)位置 - 第二步:仅在该桶内调用
equals()逐一比对
如果两个对象 equals() 返回 true,但 hashCode() 不同,它们会被分配到不同桶中,第二步根本不会触发,结果就是“明明相等却查不到”或“重复添加”。Java 规范强制要求:相等的对象必须有相等的哈希码。
验证是否写对的小技巧
写完后别急着用,快速验证几个边界情况:
- 同一个对象调用
equals(this)→ 必须为true - 传
null给equals()→ 必须为false - 两个内容完全相同的对象:既
o1.equals(o2) == true,也o1.hashCode() == o2.hashCode() - 把对象放进
HashSet,再加一个内容相同的对象 → 集合 size 不应增加










