HashSet去重依赖hashCode()和equals()组合判断,而非引用地址;默认Object的equals()用==比较,但需二者协同生效;按引用去重应优先使用Collections.newSetFromMap(new IdentityHashMap())。

HashSet 本身并不保存对象的引用地址去重,而是通过 hashCode() 和 equals() 的组合逻辑来判断是否重复。它内部用 HashMap 实现,把元素作为 key 存入,value 固定为一个空对象(PRESENT)。所以“按引用地址去重”不是 HashSet 的默认行为,也不推荐直接依赖引用地址(即 == 比较)做去重——除非你明确重写 equals/hashCode 为基于引用的判断。
为什么不能直接靠引用地址去重?
默认情况下,Object 类的 hashCode() 返回的是对象在内存中的某种映射(不一定是真实地址),equals() 默认用 == 比较引用。但 HashSet 在添加元素时:
- 先调用对象的 hashCode() 计算桶位置;
- 若桶中已有元素,再用 equals() 判断是否相等;
- 只有 hashCode 相同 且 equals 返回 true,才认为重复。
所以即使两个对象是不同实例(引用地址不同),只要它们的 hashCode 和 equals 表现一致,HashSet 就会认为是同一个元素。
如果真想按引用地址去重,怎么办?
有几种可行方式,选哪种取决于你的实际需求:
立即学习“Java免费学习笔记(深入)”;
-
用 IdentityHashMap 包装:Java 提供了
java.util.IdentityHashMap,它的 key 比较基于 ==,而不是 equals。你可以用它模拟“引用级去重”的 Set:
Set<MyObj> refSet = Collections.newSetFromMap(
new IdentityHashMap<>()
);
refSet.add(new MyObj("a")); // 实例1
refSet.add(new MyObj("a")); // 实例2,引用不同 → 不去重,会被加入
refSet.add(refSet.iterator().next()); // 同一引用 → 不会重复添加
- 自定义类,让 equals/hashCode 基于 ==(不推荐,违反契约):
public class MyObj {
private String name;
public MyObj(String name) { this.name = name; }
@Override
public boolean equals(Object o) {
return this == o; // 强制引用比较
}
@Override
public int hashCode() {
return System.identityHashCode(this); // 注意:不能用 this.hashCode()
}
}
⚠️ 注意:这种写法虽然能实现引用去重,但严重违背 hashCode contract —— 即 equals 为 true 时 hashCode 必须相同。而这里只有 this == o 时 equals 才为 true,所以用 System.identityHashCode(this) 是安全的(它保证同一对象每次调用返回相同值),但语义上已脱离“业务相等性”,容易误导协作者。
更合理的做法:明确区分“业务唯一”和“实例唯一”
多数场景下,“去重”是业务含义驱动的,比如两个 User 对象 id 相同就应视为重复。这时你应该:
- 重写 equals() 和 hashCode(),依据业务字段(如 id、name 等);
- 确保字段不可变(或至少不影响 equals/hashCode),避免放入 HashSet 后修改导致无法查到;
- 使用 IDE 自动生成(如 IntelliJ 的 Alt+Insert → “Generate equals and hashCode”),避免手写错误。
如果某段逻辑确实需要区分“是不是同一个对象实例”,那通常不该用 Set 去约束,而应改用显式引用比较(obj1 == obj2)或用 IdentityHashMap。
小结:关键点记牢
- HashSet 去重靠的是 hashCode + equals,不是引用地址;
- 想按引用去重,优先用
Collections.newSetFromMap(new IdentityHashMap()); - 不要随意重写 equals/hashCode 为 ==,除非你清楚后果且团队共识;
- 业务去重,一定基于有意义的字段,且保持不可变性或谨慎处理变更。


















