必须同时重写equals与hashcode,且二者须保持一致:equals为true时hashcode必须相同;equals需满足自反性、对称性、传递性、一致性及对null安全;hashcode须基于equals所用全部字段计算,推荐objects.hash;字段修改后不可再用于哈希集合。

重写 equals 和 hashCode 是让自定义对象在 HashSet、HashMap、LinkedHashSet 等基于哈希的集合中正确去重的关键。只重写其中一个,或实现不合规,会导致对象逻辑相等却重复添加、查找失败,甚至破坏集合结构。
必须同时重写 equals 与 hashCode
Java 规定:如果两个对象 equals 返回 true,它们的 hashCode 必须相同;反之不成立(哈希值相同不意味相等)。违反这一约定,会使哈希集合失效:
- 只重写
equals不重写hashCode→ 相等对象可能被分配到不同桶,导致HashSet误判为“不重复”,实际存入多个副本; - 只重写
hashCode不重写equals→ 哈希一致但equals仍用默认引用比较,逻辑相等的对象仍被视为不同; -
hashCode每次返回随机值(如用new Random().nextInt())→ 同一对象多次调用哈希值不同,插入后无法被找到,集合行为完全不可预测。
equals 方法要满足五项基本原则
重写的 equals 必须是:
-
自反性:对任意非空引用
x,x.equals(x)必须返回true; -
对称性:若
x.equals(y)为true,则y.equals(x)也必须为true; -
传递性:若
x.equals(y)且y.equals(z)为true,则x.equals(z)也应为true; - 一致性:多次调用结果不变(前提是对象用于比较的字段未修改);
-
对 null 的处理:
x.equals(null)必须返回false,且不能抛NullPointerException。
典型安全写法:先判 null 和类型,再逐个比较关键业务字段(推荐用 Objects.equals(a, b) 处理 null 安全)。
hashCode 应基于 equals 中使用的相同字段计算
hashCode 的值必须由 equals 判定逻辑相等所依赖的**全部字段**共同决定。常见错误是漏掉某个参与比较的字段,或用了无关字段(如临时计算值、时间戳)。
- 推荐方式:使用
Objects.hash(f1, f2, f3),它自动处理null并生成合理分布的哈希值; - 避免手写
31 * x + y类公式(易出错),除非有特殊性能要求; - 字段一旦参与
equals和hashCode,就不应在对象加入集合后修改——否则哈希桶位置与实际值不匹配,导致无法查找或删除(即对象“丢失”)。
使用 IDE 或 Lombok 自动生成更可靠
手动编写易遗漏边界或不一致。现代开发强烈建议:
- 用 IntelliJ IDEA:右键 →
Generate…→ 选择字段 → 勾选equals()和hashCode(),它会生成符合规范的代码; - 用 Lombok:在类上加
@EqualsAndHashCode注解,默认包含所有非静态非瞬态字段;如需指定字段,写成@EqualsAndHashCode(of = {"id", "name"}); - 若类有继承关系,需谨慎——父类是否已重写?子类是否应纳入父类字段?Lombok 提供
callSuper = true参数支持。
生成后可快速验证:创建两个字段值完全相同的对象,检查 obj1.equals(obj2) 为 true 且 obj1.hashCode() == obj2.hashCode() 成立。










