HashSet去重需重写equals()和hashCode(),否则同字段对象仍被视作不同元素;LinkedHashSet可保序去重;TreeSet适用于需排序的去重场景,但不支持null且性能较低;原始类型数组需装箱或用专用集合。

为什么直接用 HashSet 去重可能出错
不是所有对象放进 HashSet 都能自动去重。关键看 equals() 和 hashCode() 是否被正确重写。比如自定义类 User,若没重写这两个方法,即使两个对象字段完全相同,HashSet 也会当成不同元素。
常见错误现象:set.add(new User("Alice", 25)) 和 set.add(new User("Alice", 25)) 后,集合大小为 2。
- 必须确保类中重写了
equals()和hashCode(),且逻辑一致 - 使用 Lombok 时,加
@EqualsAndHashCode是最简方式 - 基本类型和
String、LocalDate等 JDK 类已自带合理实现,可直接用
如何对已有 List 快速去重并保持顺序
LinkedHashSet 是兼顾去重与插入顺序的最佳选择,它底层是哈希表 + 双向链表,迭代时按添加顺序返回。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 不要用
new HashSet(list)—— 会丢失顺序 - 推荐写法:
new ArrayList(new LinkedHashSet(list)) - Java 8+ 可用流:
list.stream().distinct().collect(Collectors.toList()),但注意distinct()依赖equals()/hashCode(),且性能略低于LinkedHashSet
TreeSet 去重的适用场景和陷阱
当需要去重后**自然排序**(如升序),且元素实现了 Comparable 或传入了 Comparator,TreeSet 才有意义。它不靠 hashCode(),而是基于比较结果判断重复。
容易踩的坑:
- 元素为
null时,TreeSet直接抛NullPointerException - 若自定义比较器逻辑有误(比如
compare(a,b)和compare(b,a)符号不相反),会导致去重失败或ClassCastException - 性能比
HashSet差,时间复杂度是 O(log n),适合数据量小且强依赖排序的场景
数组或原始类型怎么去重
Java 集合框架不直接支持原始类型(如 int[]),也不能把 int 直接塞进 Set 而不装箱。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 对于
int[]:先转成Integer[]或用Arrays.stream(arr).boxed().collect(Collectors.toSet()) - 注意装箱开销——大数据量时,考虑用第三方库如 Eclipse Collections 的
IntHashSet或 Trove 的TIntHashSet -
字符串数组去重最简单:
new LinkedHashSet(Arrays.asList(strArray))
真正麻烦的从来不是“有没有去重方法”,而是对象是否定义了合理的相等性语义,以及你是否意识到顺序、空值、性能这几件事在不同 Set 实现里根本不是一回事。










