必须同步重写equals和hashcode,否则逻辑相等的对象因哈希值不同被散列到不同桶中,导致hashset去重失效、hashmap查找不到已存键等问题;二者须基于相同字段计算且遵守java规范契约。

键值对在 HashMap 或 HashSet 中“丢失”,不是数据真没了,而是你找错了地方。根本原因几乎都指向 equals() 和 hashCode() 的契约被破坏——最常见的是只重写了 equals() 却忘了同步重写 hashCode(),或者两个方法用的字段不一致。
看 key 是否真的被当作同一对象处理
先验证逻辑相等性是否成立:
- 用两个内容相同的对象(比如 new 两次的 User("Alice", 25))调用
a.equals(b),确认返回 true - 再分别调用
a.hashCode()和b.hashCode(),如果结果不同,问题就在这里——违反了“相等对象必须有相同哈希码”的硬性约定 - 特别注意:如果用了 Lombok 的
@EqualsAndHashCode,检查是否漏加了callSuper = false或字段被exclude错了
查 HashMap 内部桶分布
不用翻源码,靠几行调试代码就能定位:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
- 打印 key 的哈希值:
System.out.println(key.hashCode() & (map.capacity() - 1));(这是 JDK 8+ 数组下标计算方式) - 对比存入时的 key 和查询时的 key 计算出的下标是否一致;不一致说明它们被分到了不同桶,
get()根本不会去另一个桶里找 - 也可以用反射临时访问
map.table(仅限调试),看看目标 key 实际存在哪个桶里
检查字段一致性与可变性
即使哈希值一时相同,也可能因字段变动导致后续失效:
- 确认
hashCode()和equals()使用的字段完全相同(比如都用了name和age,没漏掉id或多加createTime) - 避免用可变字段(如
status、lastLoginTime)参与哈希计算——对象一旦放进 HashMap,这些字段就不该再改 - 若业务必须修改,应先
remove()再put(),否则哈希位置错乱,key 变成“幽灵”
用工具快速验证契约合规性
别靠肉眼比对,用现成手段交叉检验:
- IDEA 中右键 → Generate → equals() and hashCode(),勾选相同字段,让生成逻辑保持同步
- 引入
org.junit.jupiter:junit-jupiter+net.jqwik:jqwik,写属性测试自动验证自反性、对称性、传递性 - 简单手写单元测试:创建多个等价对象,放入
HashSet,断言set.size() == 1;再放进HashMap,用不同实例get()验证是否都能取到值
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










