equals 与 hashCode:为什么必须同时重写?
🚀 高频指数:★★★★★
🎯 你将收获:对象比较原理、哈希结构机制、重写陷阱、集合类行为差异、项目答题模板
一、面试开场问题
面试官:为什么重写了 equals,一定要重写 hashCode?
这是 Java 面试中最常见的陷阱题之一。
很多候选人只会回答一句:“因为要保持一致性”,但一问“为什么一致”就卡壳。
二、对象比较的三层含义
| 比较方式 | 使用关键字/方法 | 比较内容 | 是否可重写 |
|---|---|---|---|
| == | 双等号 | 比较引用地址 | 否 |
| equals() | Object 方法 | 默认比较引用,可重写 | ✅ |
| hashCode() | Object 方法 | 生成对象哈希值 | ✅ |
源码片段(来自 java.lang.Object)
public boolean equals(Object obj) {
return (this == obj);
}
public native int hashCode();
默认实现中:equals 仅判断引用是否相同,hashCode 由 JVM 生成对象地址哈希。
三、为什么必须保持一致?(核心逻辑)
HashMap、HashSet 等基于 hash 的集合工作流程:
1️⃣ 存储时:先调用 hashCode() 计算哈希槽位;
2️⃣ 若槽位有冲突,再调用 equals() 判断逻辑相等;
3️⃣ 若相等则覆盖,不相等则挂链或树化。
如果只重写 equals 不重写 hashCode:
→ hashCode 不同,对象放入不同桶,查找失败。
如果只重写 hashCode 不重写 equals:
→ 哈希碰撞后 equals 返回 false,同桶重复对象。
四、规范要求(来自《Java规范》第9章)
📘 “若两个对象通过 equals 比较相等,则它们的 hashCode 必须相同。”
但反之不要求。
| 条件 | 结果 |
|---|---|
| equals(a,b)==true → hashCode(a)==hashCode(b) | ✅ 必须成立 |
| hashCode(a)==hashCode(b) → equals(a,b)==true | ❌ 不一定成立(哈希碰撞允许) |
五、示例:错误与正确写法
❌ 错误示例
class User {
String name;
User(String name) { this.name = name; }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User u = (User) o;
return Objects.equals(name, u.name);
}
}
User u1 = new User("Tom");
User u2 = new User("Tom");
Set<User> set = new HashSet<>();
set.add(u1);
set.add(u2);
System.out.println(set.size()); // 输出 2 ❌
✅ 正确示例
@Override
public int hashCode() {
return Objects.hash(name);
}
输出结果:set.size() = 1 ✅
六、常见重写模板
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof User)) return false;
User user = (User) o;
return Objects.equals(id, user.id);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
📌 推荐使用
Objects.hash(),避免手动拼接。
七、面试官追问清单
| 面试问题 | 答题要点 |
|---|---|
| 为什么要同时重写? | Hash集合需要 equals 确认逻辑相等,hashCode 定位桶。 |
| hashCode 一样 equals 一定一样吗? | 否,哈希碰撞可能。 |
| String 的 equals 和 hashCode 如何实现? | equals 比较字符数组,hashCode 缓存计算值。 |
| Integer 的 hashCode 是什么? | 直接返回 int 值本身。 |
| HashSet 判断元素重复靠什么? | hashCode 定位桶 + equals 判断逻辑相等。 |
八、口诀记忆
☕️ “哈希定位,等号确认;相等哈同,不同可冲。”
这句口诀能让你在面试中脱口而出,一句话解释核心逻辑。
九、项目应用场景
举例:
在多线程缓存或集合去重场景中,若对象未正确重写 hashCode,会导致:
- HashMap 重复键;
- Set 中出现“看似相等”的多份对象;
- Redis 或数据库映射 Key 不一致。
👉 实际开发中建议:
- 使用 Lombok 的 @EqualsAndHashCode;
- 或使用 IDE 自动生成(如 IntelliJ IDEA → Alt+Insert → equals/hashCode)。
十、小结
| 知识点 | 要点总结 |
|---|---|
| equals 默认比较引用 | 可重写为逻辑比较 |
| hashCode 默认返回地址哈希 | 可重写为逻辑哈希 |
| 两者必须一致 | equals 相等 ⇒ hashCode 相同 |
| 典型应用 | HashMap / HashSet / LinkedHashMap |
✅ 口诀复盘:
“逻辑相等需双修,哈希映射靠同步。”

被折叠的 条评论
为什么被折叠?



