重写 equals 时必须同步重写 hashcode,这是 java 对象契约的硬性要求;若不遵守,hashmap、hashset 中会出现查找失败、重复添加或对象“消失”等必然现象。

重写 equals 时必须同步重写 hashCode,不是“建议”,而是 Java 对象契约的硬性要求。否则对象放进 HashMap、HashSet 等集合后,会出现查不到、重复添加、甚至“凭空消失”的现象——这不是 Bug,是契约被破坏后的必然结果。
核心原则:字段一致、顺序一致、稳定性优先
参与 equals 判断的每一个字段,必须且仅这些字段,参与 hashCode 计算;顺序也要保持一致(尤其是用 Objects.hash() 时);所有字段值在对象存入哈希集合后不能被修改,否则哈希码变化会导致查找失败。
- 比如
id和name参与了equals比较,那hashCode中就必须只用这两个字段,不多不少 - 若误把
createTime或status加进hashCode,但equals并不比它们,就违反契约 - 字段顺序不同(如
Objects.hash(name, id)vsObjects.hash(id, name))虽不报错,但可能影响哈希分布,建议严格对齐equals中的判断顺序
推荐写法:用 Objects.equals 和 Objects.hash
避免手写 null 判断和类型转换错误,JDK 7+ 提供了安全、简洁的工具方法:
-
equals中用Objects.equals(a, b)替代a == null ? b == null : a.equals(b),自动处理 null -
hashCode中用Objects.hash(f1, f2, f3)替代手动拼接,它内部已做 null 安全处理,并保证相同字段组合生成相同哈希值 - 类型检查统一用
getClass() != obj.getClass(),不用instanceof,防止子类比较破坏对称性
常见踩坑点
这些错误看似微小,却极易引发静默故障:
- 只改
equals,忘记改hashCode→ 对象进HashSet后contains()返回 false,即使equals为 true - 在
hashCode中调用懒加载 getter 或计算型方法 → 可能抛异常,或返回不稳定值,导致哈希表操作中途崩溃 - 用可变字段(如
balance、lastLoginTime)参与hashCode→ 对象加入HashSet后修改该字段,就再也找不到了 - 在
equals中用了instanceof,而子类也重写了equals→ 出现a.equals(b) == true但b.equals(a) == false,破坏对称性
IDE 辅助不是万能,但值得依赖
IntelliJ 或 Eclipse 的“Generate hashCode and equals”功能非常可靠,它会自动提取所有参与比较的字段、插入 Objects 工具调用、加上类型和 null 检查。但要注意:
- 生成前确认勾选的字段和你业务定义的“逻辑相等”完全一致
- 生成后别手动删字段或改顺序,除非你清楚每一步的影响
- 如果类有继承关系,需额外判断是否应允许父类字段参与比较
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











