Java equals 与 hashCode
对象相等性与哈希契约:何时覆写 equals/hashCode,如何避免哈希容器中的丢失与重复。
#type / concept
#status / growing
#tech / dev
#resource / java
[!info] 关联笔记
Java equals 与 hashCode
这个概念为什么出现
业务常问“是不是同一笔订单/同一个用户”。== 只回答引用是否同一;equals 回答业务相等。HashMap/HashSet 还依赖 hashCode 分桶——破坏契约会导致“放得进、取不出”。
[!abstract] 一句话理解
equals定义业务相等;hashCode必须与 equals 一致;哈希容器先 hash 后 equals,契约破坏即逻辑漏洞。
最小可运行示例
场景:优惠券核销用“券码”作为集合去重键
import java.util.HashSet;
import java.util.Objects;
import java.util.Set;
final class Coupon {
private final String code;
Coupon(String code) {
this.code = Objects.requireNonNull(code);
}
@Override public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Coupon c)) return false;
return code.equals(c.code);
}
@Override public int hashCode() {
return code.hashCode();
}
@Override public String toString() { return code; }
}
public class CouponDedup {
public static void main(String[] args) {
Set<Coupon> set = new HashSet<>();
set.add(new Coupon("SAVE50"));
set.add(new Coupon("SAVE50")); // 业务相等,应去重
System.out.println(set.size()); // 1
System.out.println(set.contains(new Coupon("SAVE50"))); // true
}
}
建议运行:
javac CouponDedup.java && java CouponDedup
期望输出:
1
true
结合场景再看三个关注点
- 只覆写 equals 不覆写 hashCode 会让 HashSet 失效。
- 相等字段必须稳定;可变键进哈希容器是坑。
instanceof模式比错误的 getClass 策略要结合继承设计选择。
核心概念与准确模型
1. equals 契约(JLS)
- 自反、对称、传递、一致
- 对
null返回 false
2. hashCode 契约
equals为 true ⇒hashCode必须相等- 不相等对象 hash 可碰撞,但应尽量分散
3. == / equals / Objects.equals
| 场景 | 用法 |
|---|---|
| 同一实例 | == |
| 业务相等 | equals |
| 两侧可能 null | Objects.equals(a,b) |
4. Record 与自动生成
record默认按组件生成 equals/hashCode(见 Record 篇)
边界情况与反直觉行为
- 继承体系中 equals 对称性极难维护——值对象宜
final。 - 浮点
NaN、数组equals默认比引用(应用Arrays.equals)。 - 字符串比较注意大小写/Locale。
常见误区
[!warning] 常见误区:用可变字段做 equals 且作 HashMap key 变更字段后对象落到错误桶,表现为“丢失”。
工程实践
- 值对象:
final类 + 不可变字段 + 成对覆写。 - 实体对象:常按 ID 相等,谨慎混用。
- 用 IDE/Record 生成,再人工审查空值与数组。
- 单测覆盖对称性与哈希集合行为。
本节总结
- 相等是业务定义,不是引用默认
- hashCode 与 equals 一体
- 不可变值对象最省心
自测题
- 为何 equals 相同必须 hashCode 相同?
Objects.equals解决什么?
参考答案
- 哈希容器用 hash 定位桶;不一致会导致找不到相等对象。
- 空安全的相等比较,避免
a.equals(b)在 a 为 null 时 NPE。
延伸阅读与资料来源
| 资料 | 类型 | 支撑内容 |
|---|---|---|
| Object.equals / hashCode | API | 契约说明 |
| JLS Object Equality 相关与 API 文档 | 规范/API | 相等语义 |
| Effective Java(条目 equals/hashCode) | 权威书 | 实践规则 |