为什么重写 equals 必须重写 hashCode

一句话先行:

你改了“怎么判相等”,就必须同步改“怎么分桶”。


一、先立三条铁律(这是 JVM 的宪法)

Java 对 equalshashCode 有明确约定:

1️⃣ equals 相等 → hashCode 必须相等
2️⃣ hashCode 相等 → equals 不一定相等
3️⃣ 只要参与哈希容器,就必须遵守 1️⃣

这不是建议,是契约


二、Object 默认实现在干嘛?

equals

1
2
3
public boolean equals(Object obj) {
return this == obj;
}

👉 比的是 内存地址

hashCode

1
public native int hashCode();

👉 基于 对象内存特征(你可以理解成“身份证号”)

结论:

默认情况下:
同一个对象才相等,不同对象永远不相等


三、问题从哪里开始?

你在业务里重写了 equals 👇

1
2
3
4
5
6
7
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(name, user.name);
}

含义是:

只要 name 一样,我就认为两个对象相等

但你没重写 hashCode


四、灾难发生在 HashMap 里

HashMap 的查找逻辑是这样的(重点):

1
2
1️⃣ 先算 hashCode → 找桶
2️⃣ 桶里再用 equals 比

现在来看结果:

对象 name equals hashCode
A neo true ❌ 不同
B neo true ❌ 不同

结果就是:

  • equals:你说它们相等 ✅
  • hashCode:JVM 说它们不是一类 ❌

👉 被分到了不同桶

👉 HashMap 当成两个元素

👉 违背第一条铁律


五、这就是为什么“必须一起重写”

因为你改了这件事:

“什么叫相等”

却没改这件事:

“相等的对象该去哪一个桶”

一句总结:

equals 决定“是不是同一个人”
hashCode 决定“住哪栋楼”
你不能只改身份证,不改住址


六、正确姿势(记这个就够了)

规则口诀

参与 equals 的字段,必须全部参与 hashCode

标准写法

1
2
3
4
@Override
public int hashCode() {
return Objects.hash(name);
}

或者:

1
2
3
4
@Override
public int hashCode() {
return name == null ? 0 : name.hashCode();
}

七、一句话终极总结

重写 equals 却不重写 hashCode
就等于告诉 HashMap:
“他们是同一个人,但你别把他们放一起。”

HashMap:🤯
Bug:😈