2.equals 与 hashCode:为什么必须同时重写?

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

✅ 口诀复盘:
“逻辑相等需双修,哈希映射靠同步。”

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

愤怒的代码

如果您有受益,欢迎打赏博主😊

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值