必须同时重写equals()和hashCode(),因为HashSet基于HashMap实现:先用hashCode()定位桶,再用equals()在桶内比较;若只重写equals(),相同对象可能散列到不同桶,导致去重失效。

Java 中 HashSet 去重自定义对象,关键在于正确重写 equals() 和 hashCode() 方法。否则即使两个对象逻辑上“相同”,HashSet 也会把它们当作不同元素存入。
为什么必须同时重写 equals 和 hashCode
HashSet 底层基于 HashMap 实现,添加元素时:
- 先用
hashCode()计算哈希值,决定存入哪个桶(bucket) - 再在该桶内调用
equals()逐个比较,确认是否已存在相同元素
如果只重写 equals() 不重写 hashCode(),相同对象可能被散列到不同桶里,equals() 根本不会被调用,去重就失效了。
重写规则要严格遵守
两个方法必须保持一致性:
- 如果
a.equals(b)返回true,那么a.hashCode()必须等于b.hashCode() - 对象在
HashSet中使用期间,只要参与equals比较的字段没变,hashCode()就不能变(所以通常不建议用可变字段参与计算) -
hashCode()不必唯一,但尽量均匀分布,减少哈希冲突
一个完整示例(以 Person 类为例)
假设按 name 和 age 判定是否为同一人:
public class Person {
private String name;
private int age;
<pre class="brush:php;toolbar:false;">public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return age == person.age && Objects.equals(name, person.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}}
使用方式:
Set<person> set = new HashSet();
set.add(new Person("Alice", 25));
set.add(new Person("Alice", 25)); // 这个不会添加成功
System.out.println(set.size()); // 输出 1</person>IDE 可以帮你快速生成(推荐)
IntelliJ IDEA 或 Eclipse 都支持自动生成:
- 右键 → Generate → 选 Equals and HashCode
- 勾选参与比较的字段(如
name、age),点击确定 - 生成的代码会自动处理 null 安全、类型检查和哈希计算
不建议手写 hashCode(比如用 name.hashCode() * 31 + age),容易出错;用 Objects.hash(...) 更简洁安全。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南











