要让自定义对象在HashSet中不重复,必须同时重写equals()和hashCode()方法并保持逻辑一致:先通过hashCode()定位桶,再用equals()比较内容;二者须基于相同不可变字段,且符合自反性、对称性、传递性、一致性及null安全规范。

要让自定义对象在 HashSet 中不重复,关键在于正确重写 equals() 和 hashCode() 方法,并确保二者逻辑一致。
必须同时重写 equals() 和 hashCode()
HashSet 底层基于 HashMap 实现,判断是否重复分两步:
- 先用
hashCode()计算哈希值,快速定位到对应的桶(bucket) - 再在该桶内调用
equals()逐个比较对象内容
如果只重写 equals() 而不重写 hashCode(),相同逻辑的对象可能被分配到不同桶中,导致 HashSet 误判为“不重复”;反之,只重写 hashCode() 而不重写 equals(),则无法准确判断内容是否真的一致。
equals() 的实现要点
遵循《Effective Java》推荐的规范:
立即学习“Java免费学习笔记(深入)”;
- 自反性:对任何非 null 引用
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
常用写法(以 User 类为例):
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return Objects.equals(id, user.id) && Objects.equals(name, user.name);
}
hashCode() 的实现要点
核心原则:**只要两个对象 equals() 返回 true,它们的 hashCode() 就必须相等**。
推荐用 Objects.hash(...) 自动生成,它会自动处理 null 安全和字段组合:
@Override
public int hashCode() {
return Objects.hash(id, name);
}
注意:参与 hashCode() 和 equals() 计算的字段必须是**不可变的**(如 final 字段),或至少在对象加入 HashSet 后不再修改。否则哈希值变化会导致对象“丢失”——再也无法被 contains() 找到,也无法被 remove() 删除。
IDE 可以帮你生成(但得检查)
IntelliJ IDEA 或 Eclipse 都支持自动生成 equals() 和 hashCode()。生成后务必确认:
- 选中的字段确实是业务上决定“相等性”的字段(比如通常不含
createTime、status等可变或无关字段) - 没有遗漏关键字段,也没有多加字段
- 类没有继承自某个已重写过这两个方法的父类,否则可能需要调用
super.equals()/super.hashCode()
不复杂但容易忽略。


















