重写 hashcode 必须遵守三项契约:一致性(同一对象多次调用返回相同值)、等价性(equals 为 true 则 hashcode 必须相等)、稳定性(不依赖可变状态或运行时变量);计算字段须与 equals 完全一致,推荐使用 objects.hash。

重写 hashCode 方法必须严格遵循 Java 的契约,核心是:只要两个对象 equals 返回 true,它们的 hashCode 就必须返回相同整数;反之不强制,但应尽量减少冲突。
必须满足的三项基本契约
这三条是 JVM 规范明确定义、不可妥协的前提:
-
一致性:同一对象在未修改参与
equals判断的字段前提下,多次调用hashCode()必须返回相同值 -
等价性约束:若
a.equals(b) == true,则a.hashCode() == b.hashCode()必须成立 - 稳定性:哈希码不能依赖运行时状态(如随机数、当前时间、线程 ID)或可变字段(如计数器、缓存标记)
与 equals 方法必须保持字段同步
hashCode 的计算字段必须且仅限于 equals 中实际参与比较的那些非静态字段。多一个、少一个、顺序不一致,都会破坏契约。
- 例如
equals只比对id和name,那hashCode就只能用这两个字段——createdAt、status、version等无关字段不能加入 - 推荐统一使用
Objects.hash(id, name),它自动处理null,且与Objects.equals(id, other.id) && Objects.equals(name, other.name)完全对应 - 避免手写
31 * id + name.hashCode()这类逻辑,易出错且难维护
类型判断要与 equals 严格一致
如果 equals 用 getClass() == obj.getClass() 做类型检查(推荐做法),hashCode 就不能基于继承关系做宽松处理;反之亦然。
- 用
instanceof在父类中重写equals,可能让子类和父类互等,但破坏对称性——此时hashCode若仍只按父类字段算,就可能让逻辑相等的对象哈希不同 - 稳妥方案:父子类都独立重写,且各自
hashCode仅覆盖自身equals所含字段
常见错误及规避方式
这些做法看似省事,实则直接导致 HashMap 查不到、HashSet 重复添加等线上问题:
- ❌ 返回常量(如
return 1;):所有对象哈希相同,集合退化为链表,性能崩坏 - ❌ 使用
Math.random()或System.nanoTime():违反一致性契约 - ❌ 在
hashCode中调用可能改变状态的方法(如懒加载初始化):字段未变但哈希变了 - ✅ 正确姿势:用
Objects.hash(field1, field2, ...),字段列表与equals完全一致,且全部为不可变或业务稳定字段
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











