hashset实现自定义对象去重的关键是同时正确重写equals()和hashcode()方法,二者必须保持一致:equals返回true时hashcode必须相同,且参与比较的字段需完全一致;推荐用ide自动生成,并优先采用不可变设计。

HashSet 实现自定义对象去重,关键在于**正确重写 equals() 和 hashCode() 方法**。HashSet 底层基于 HashMap,添加元素时先用 hashCode() 定位桶位置,再用 equals() 判断是否已存在相同元素。如果这两个方法没重写或重写不一致,即使逻辑上“内容相同”的对象也会被当作不同对象存入。
必须同时重写 equals 和 hashCode
只重写 equals() 不行:HashSet 先算 hash 值,若 hash 不同直接放入不同桶,根本不会调用 equals();只重写 hashCode() 也不行:所有对象 hash 值一样会全挤在一个桶里,但 equals() 还是用 Object 默认实现(比较内存地址),依然无法识别逻辑相等。
- 两个对象
equals()返回true,它们的hashCode()必须返回相同整数 - 两个对象
equals()返回false,hashCode()可以相同也可以不同(但尽量不同,提升性能) - 同一对象多次调用
hashCode()必须返回相同值(只要参与计算的字段没变)
推荐用 IDE 自动生成(避免手写出错)
IntelliJ IDEA 或 Eclipse 都支持一键生成符合契约的 equals() 和 hashCode()。以一个 Person 类为例:
// 示例:只用 name 和 age 判定是否重复
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
public class Person {
private String name;
private int age;
// 构造、getter 省略...
@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);
}
}
注意:参与比较的字段必须和 hashCode() 中使用的字段完全一致;如果字段可能为 null,用 Objects.equals() 和 Objects.hash() 更安全。
不可变性更省心(强烈建议)
如果对象创建后字段不再修改(即把 name、age 设为 final),就能彻底避免因字段变更导致 hash 值变化的问题——否则,对象加入 HashSet 后若修改了参与 hash 计算的字段,它就再也找不到了(因为 hash 桶位置变了,原位置又找不到它)。
- 加
final修饰符,构造时赋值 - 不提供对应的 setter 方法
- 如果必须可变,确保修改字段前先从 HashSet 中 remove,改完再 add 回去(不推荐,易出错)
验证是否生效(简单测试)
写个测试确认去重逻辑正常:
Set<person> set = new HashSet();
set.add(new Person("Alice", 25));
set.add(new Person("Alice", 25)); // 内容相同,应被去重
set.add(new Person("Bob", 30));
System.out.println(set.size()); // 输出 2</person>
如果输出是 3,说明 equals/hashCode 没起作用,回头检查生成逻辑或字段一致性。
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










