java hashset 去重依赖 hashcode 和 equals 两个方法:先调用 hashcode 确定元素在哈希表中的桶位置,再在该桶内用 equals 逐个比对是否已存在。只要重写这两个方法并保持逻辑一致,就能让自定义对象按业务键值正确去重。

hashCode 冲突时 equals 如何工作
当不同对象的 hashCode 返回相同值时,HashSet 会在同一个桶里遍历链表或红黑树,对每个已有元素调用 equals 进行精确匹配。只有 equals 返回 true 才视为重复,否则插入新节点。

import java.util.HashSet;
import java.util.Objects;
class Person {
private final String name;
private final int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Person)) return false;
Person other = (Person) o;
return age == other.age && Objects.equals(name, other.name);
}
}
public class Main {
public static void main(String[] args) {
HashSet<person> set = new HashSet();
set.add(new Person("Alice", 30));
set.add(new Person("Bob", 25));
set.add(new Person("Alice", 30)); // 重复,应被忽略
System.out.println(set.size()); // 输出 2
}
}</person>
未重写 equals 导致去重失败
如果只重写 hashCode 而忘记重写 equals,HashSet 会使用 Object 类的默认 equals(即引用相等)。即使两个对象内容完全相同,只要不是同一个实例,就会被当作不同元素插入,导致去重失效。
你是 TypeScript 项目架构专家,专注于脚手架搭建生产就绪的 Node.js 和前端应用。生成包含现代工具链(pnpm、Vite、Next.js)、类型

import java.util.HashSet;
class BadPerson {
private final String name;
private final int age;
public BadPerson(String name, int age) {
this.name = name;
this.age = age;
}
@Override
public int hashCode() {
return name.hashCode() * 31 + age;
}
// 注意:没有重写 equals
}
public class Main2 {
public static void main(String[] args) {
HashSet<badperson> set = new HashSet();
set.add(new BadPerson("Alice", 30));
set.add(new BadPerson("Alice", 30)); // 内容相同但引用不同
System.out.println(set.size()); // 输出 2,去重失败
}
}</badperson>
修改可变字段后破坏哈希一致性
如果作为 HashSet 元素的对象包含可变字段,且在加入集合后修改了参与 hashCode 计算的字段,会导致该对象的哈希值改变。此时 HashSet 无法再通过原哈希值定位到该元素,可能引发查找失败、内存泄漏或并发修改异常。因此,用于集合的元素应尽量使用不可变类型或避免在加入集合后修改关键字段。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










