外观
一句话答案
重写 equals 必须重写 hashCode,相等对象 hashCode 必须相同,HashMap 先比较 hashCode 再比较 equals。
核心要点
原因:equals() 和 hashCode() 之间有契约(Contract):
如果两个对象
equals()返回true,则它们的hashCode()必须相同。
如果只重写 equals() 不重写 hashCode():
java
class Person {
String name;
@Override
public boolean equals(Object o) {
return this.name.equals(((Person) o).name);
}
// 没有重写 hashCode(),使用默认的 Object.hashCode()(基于内存地址)
}
Person p1 = new Person("Alice");
Person p2 = new Person("Alice");
p1.equals(p2); // true(重写了 equals)
p1.hashCode() == p2.hashCode(); // false!(内存地址不同)
// 放入 HashMap 后出问题:
Map<Person, String> map = new HashMap<>();
map.put(p1, "value");
map.get(p2); // 返回 null!因为 p2 的 hashCode 不同,定位到错误的桶HashMap 的查找流程:
- 计算 key 的
hashCode()→ 定位桶位置 - 在桶中用
equals()逐个比较
如果 hashCode() 不一致,第一步就定位错误,永远找不到对应的 key。
正确做法:
java
@Override
public int hashCode() {
return Objects.hash(name); // 基于 name 计算,保证 equals 相同时 hashCode 也相同
}追问与易错
追问方向:
- 为什么重写 equals 必须重写 hashCode?(HashMap 先比 hashCode 再比 equals)
- hashCode 相同 equals 一定相同吗?(不一定,哈希碰撞)
- HashMap 中 key 的 hashCode 变了会怎样?(找不到原来的 entry,内存泄漏)
易错点:
- ❌ 反过来说"equals 相等 hashCode 可以不同"——这会违反契约,HashMap 行为异常
- ❌ ��记 null 值处理——equals 实现中要处理 null 和类型检查
💡 记忆锚点
HashMap找人两步走:先按hashCode定楼层(桶),再用equals逐户敲门。equals说相等但hashCode指错楼层,就永远敲不到门——所以重写equals必须同步重写hashCode。