HashSet能自动去重因其底层基于HashMap,add时通过hashCode()和equals()判断重复;自定义类需重写这两个方法;若需有序去重应选LinkedHashSet。

Java 中用 HashSet 去重最直接的方式就是把数据添加到 HashSet 实例中,它会自动忽略重复元素,因为其底层基于哈希表,不允许重复键(即重复对象)。
为什么 HashSet 能自动去重
HashSet 内部使用 HashMap 存储元素,每个元素作为 HashMap 的 key。当调用 add() 方法时,会先计算对象的 hashCode(),再结合 equals() 判断是否已存在相同元素。只有两个方法都满足“同一对象”逻辑,才视为重复。
- 自定义类去重时,必须重写
hashCode()和equals() - 对于
String、Integer等 JDK 内置类型,已默认实现,可直接使用 - 注意:
HashSet不保证插入顺序,如需有序去重,考虑LinkedHashSet
基础去重示例(字符串/基本类型)
适用于 List 中存的是不可变对象或 JDK 常见类型:
List<String> list = Arrays.asList("a", "b", "a", "c", "b");
Set<String> uniqueSet = new HashSet<>(list);
List<String> uniqueList = new ArrayList<>(uniqueSet); // 转回 List(顺序不保证)
如果希望保留原始顺序,改用 LinkedHashSet:
立即学习“Java免费学习笔记(深入)”;
Set<String> uniqueSet = new LinkedHashSet<>(list); // 插入顺序被保留
自定义对象去重(关键!)
比如有一个 User 类,按 id 去重:
public class User {
private Long id;
private String name;
public User(Long id, String name) {
this.id = id;
this.name = name;
}
@Override
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);
}
@Override
public int hashCode() {
return Objects.hash(id);
}
}
之后即可正常使用:
List<User> users = Arrays.asList(
new User(1L, "Alice"),
new User(2L, "Bob"),
new User(1L, "Alice2") // id 相同,会被去重
);
Set<User> uniqueUsers = new HashSet<>(users); // 最终只保留一个 id=1 的 User
一行流式去重(Java 8+)
配合 Stream API,简洁高效:
List<String> uniqueList = list.stream()
.distinct()
.collect(Collectors.toList());
注意:distinct() 内部正是基于 HashSet 实现的,所以同样依赖 hashCode/equals 正确性。
不复杂但容易忽略细节,核心就三点:选对集合类型、确保对象能正确比较、理解顺序与线程安全限制(HashSet 非线程安全,多线程场景请用 Collections.synchronizedSet() 或 ConcurrentHashMap.newKeySet())。


















