用 new HashSet(list) 最简单直接,但不保证顺序;需保持插入顺序时应使用 LinkedHashSet;自定义类必须重写 equals() 和 hashCode() 才能正确去重。

用 new HashSet(list) 最简单直接
只要 List 元素类型重写了 equals() 和 hashCode(),就能正确去重。这是最常用、开销最小的方式。
- 适用于
String、Integer等 JDK 内置类型,它们已正确定义了哈希逻辑 - 自定义类必须手动重写
equals()和hashCode(),否则所有对象都被视为不等,去重失效 - 不保证顺序 ——
HashSet是无序的,如果需要按插入顺序保留,改用LinkedHashSet
需要保持插入顺序?用 LinkedHashSet
LinkedHashSet 在去重的同时维护元素首次添加的顺序,适合对顺序敏感的场景(比如展示列表、日志去重后仍要按时间先后)。
Listlist = Arrays.asList("a", "b", "a", "c"); Set set = new LinkedHashSet<>(list); // 结果:[a, b, c]
- 性能略低于
HashSet(因额外维护链表),但差别极小,日常使用可忽略 - 不能用
TreeSet替代 —— 它按自然序或比较器排序,不是按插入顺序 - 注意:重复元素只保留第一次出现的那个,后续相同值被跳过
Stream + Collectors.toSet() 的陷阱
Java 8+ 可用流式写法:list.stream().collect(Collectors.toSet()),但结果类型是 Set 接口,实际返回的是 HashSet 实例(JDK 12+ 可能是不可变集合,取决于具体实现)。
- 无法控制底层实现 —— 你不能指定它用
LinkedHashSet或TreeSet - 如果需要特定行为(如有序、线程安全、自定义比较),必须显式构造目标
Set子类 - 空
List不会报错,但若List含null元素,而目标Set不允许(如TreeSet),会抛NullPointerException
自定义类去重必须重写 equals() 和 hashCode()
这是最容易被忽略、也最常导致“去重失败”的原因。仅靠 == 判断引用相等,所有对象都不同。
立即学习“Java免费学习笔记(深入)”;
public class User {
private String name;
private int age;
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
User user = (User) o;
return age == user.age && Objects.equals(name, user.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);
}
}
- IDE(如 IntelliJ)可自动生成这两个方法,别手写
- 字段选错、漏字段、用了可变字段(如
ArrayList字段)都会导致哈希不稳定 - 如果类是
record(Java 14+),默认已生成正确实现,无需额外操作
equals()/hashCode(),或者误以为 TreeSet 能保持插入顺序。










