必须同时重写equals和hashCode,因为HashMap先用hashCode定位桶,再用equals判断是否相等;若只重写其一,会导致逻辑相等的key被误判为不同key,引发重复插入、查不到值等问题。

Java 中 Map 出现“重复 Key”现象,往往不是 Map 本身出错,而是 Key 的 equals 和 hashCode 实现不合规,导致 HashMap/HashSet 等基于哈希的集合行为异常。排查核心在于验证这两个方法是否满足契约:相等的对象必须有相同哈希码,且 equals 具备自反性、对称性、传递性、一致性,以及对 null 的安全处理。
检查 equals 和 hashCode 是否成对重写
常见错误是只重写 equals 却忘了重写 hashCode,或反之。JVM 不会强制要求两者同时存在,但 HashMap 查找时先用 hashCode 定位桶(bucket),再用 equals 比较具体元素。若两个逻辑相等对象 hashCode 不同,它们会被分到不同桶中,Map 就会误判为两个不同 Key。
- 确认类中是否同时存在
public boolean equals(Object o)和public int hashCode()方法 - 若使用 IDE(如 IntelliJ IDEA),右键 → “Generate” → 勾选 both,可自动生成符合规范的实现
- 避免手写
hashCode仅返回常量(如return 1;),这虽不报错,但会退化为链表遍历,且仍可能因equals不严谨导致重复
验证 equals 是否满足基本契约
尤其注意 equals 方法中是否做了非空判断、类型检查、字段比较逻辑是否覆盖所有关键属性,以及是否调用了父类 equals(如有继承)。
- 开头必须有
if (this == obj) return true;和if (obj == null || getClass() != obj.getClass()) return false; - 比较字段时,对引用类型优先用
Objects.equals(a, b)(自动处理null),而非a.equals(b)(可能 NPE) - 若类继承自非 final 类(如
java.util.Date),需考虑协变 equals,通常建议避免继承可变类作为 Key
用单元测试快速暴露问题
写几个小而明确的测试用例,比在业务逻辑里反复调试更高效:
立即学习“Java免费学习笔记(深入)”;
- 创建两个字段值完全相同的实例,验证
obj1.equals(obj2) == true且obj1.hashCode() == obj2.hashCode() - 创建两个仅某个非 Key 字段不同的实例(如 DTO 中带 version 字段),确认它们仍应
equals为 true(如果该字段不影响业务唯一性) - 把两个相等对象先后放入
HashMap,检查map.size()是否为 1,且第二次put返回前一个 value(说明被替换而非新增)
借助工具辅助诊断
运行时发现问题后,可临时加日志或用调试器观察行为:
- 在
equals和hashCode方法第一行加System.out.println("equals called: " + this + " vs " + obj);,看是否被调用、参数是否符合预期 - 用 IDE 调试时,在
HashMap.put(K,V)断点处,展开内部Node[] table,观察两个疑似“重复”的 Key 是否落在同一桶中;若不在同一桶,基本可断定hashCode不一致 - 使用 Lombok 的
@EqualsAndHashCode注解可减少手写错误(注意指定include或exclude字段)


















