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

结合场景再看三个关注点

  1. 只覆写 equals 不覆写 hashCode 会让 HashSet 失效。
  2. 相等字段必须稳定;可变键进哈希容器是坑。
  3. instanceof 模式比错误的 getClass 策略要结合继承设计选择。

核心概念与准确模型

1. equals 契约(JLS)

  • 自反、对称、传递、一致
  • null 返回 false

2. hashCode 契约

  • equals 为 true ⇒ hashCode 必须相等
  • 不相等对象 hash 可碰撞,但应尽量分散

3. == / equals / Objects.equals

场景用法
同一实例==
业务相等equals
两侧可能 nullObjects.equals(a,b)

4. Record 与自动生成

  • record 默认按组件生成 equals/hashCode(见 Record 篇)

边界情况与反直觉行为

  1. 继承体系中 equals 对称性极难维护——值对象宜 final
  2. 浮点 NaN、数组 equals 默认比引用(应用 Arrays.equals)。
  3. 字符串比较注意大小写/Locale。

常见误区

[!warning] 常见误区:用可变字段做 equals 且作 HashMap key 变更字段后对象落到错误桶,表现为“丢失”。

工程实践

  1. 值对象:final 类 + 不可变字段 + 成对覆写。
  2. 实体对象:常按 ID 相等,谨慎混用。
  3. 用 IDE/Record 生成,再人工审查空值与数组。
  4. 单测覆盖对称性与哈希集合行为。

本节总结

  • 相等是业务定义,不是引用默认
  • hashCode 与 equals 一体
  • 不可变值对象最省心

自测题

  1. 为何 equals 相同必须 hashCode 相同?
  2. Objects.equals 解决什么?
参考答案
  1. 哈希容器用 hash 定位桶;不一致会导致找不到相等对象。
  2. 空安全的相等比较,避免 a.equals(b) 在 a 为 null 时 NPE。

延伸阅读与资料来源

资料类型支撑内容
Object.equals / hashCodeAPI契约说明
JLS Object Equality 相关与 API 文档规范/API相等语义
Effective Java(条目 equals/hashCode)权威书实践规则
创建于 2026/7/15 更新于 2026/7/15