必须同时重写hashCode()和equals(),因为HashMap先用hashCode()定位桶再用equals()精确匹配;若只重写其一,会导致存取异常或性能退化;二者须满足相等必同哈希、字段一致、拒绝可变字段等契约。

Java 中 HashMap 要正确识别键的“逻辑相等性”,必须同时重写 hashCode() 和 equals(),且二者逻辑严格一致。否则会出现“存得进、取不出”“重复插入相同业务键”等隐蔽问题。
为什么必须两个一起重写
HashMap 查找键的过程分两步:
- 先调用
hashCode()算出哈希值,定位到对应桶(数组下标) - 再在该桶的链表或红黑树中,逐个调用
equals()判断是否真正相等
如果只重写 equals(),对象可能被散列到不同桶里,equals() 根本没机会执行;如果只重写 hashCode(),所有对象哈希值相同,全挤在一个桶里,退化为线性遍历,且仍按默认引用比较——等于白改。
重写的核心规则
这两个方法必须遵守 Java 规范契约:
当用户要求为 Java 代码添加注释、修改注释、审查注释、整理注释格式,或要求按照代码注释规范处理 Java 文件时,使用本技能。本技能用于统一生成和维护 Java 类注释模板、按 Git 对比 release 判断版本号、补充每行代码上方中文块注释、调整注释空行规则,并保持 Java 文件 UTF-8 无 BOM 编码。典型触发场景包括:补充类注释、更新 @version、检查 @author 来源、整理行尾注释、把注释放到代码上一行、检查注释是否符合规范、构建前检查 Java 注释风格。
立即学习“Java免费学习笔记(深入)”;
-
相等必同哈希:若
a.equals(b) == true,则a.hashCode() == b.hashCode()必须成立 -
哈希可冲突:
a.hashCode() == b.hashCode()不代表a.equals(b)为 true -
字段一致性:参与
equals()比较的所有字段,必须全部参与hashCode()计算 - 拒绝可变字段:不能用会修改的字段(如 status、lastModifiedTime),否则对象入 Map 后一旦修改,哈希位置就错位,再也查不到
推荐写法与工具实践
避免手写易错的公式(如 31 * x + y),直接使用标准方案:
- 用
Objects.equals(a, b)替代a != null && a.equals(b),自动处理 null 安全 - 用
Objects.hash(f1, f2, f3)生成哈希值,简洁、均匀、防空 - IDE 自动生成(IntelliJ/Eclipse)后务必检查:是否勾选了全部关键字段?是否排除了时间戳、临时状态等无关字段?
- 在
equals()开头加if (this == obj) return true;提升性能
必须验证的行为
别只看编译通过,要写小测试确认 Map 行为正常:
- 创建两个字段完全相同的对象
p1和p2 -
map.put(p1, "v1");后调用map.get(p2)→ 应返回"v1" - 再次
map.put(p2, "v2")后查看map.size()→ 应仍为1(覆盖而非新增)
任一不满足,说明重写有缺陷,需回头检查字段选择和实现一致性。

















