LinkedHashSet是Java中保留顺序去重最常用方法,基于哈希表+双向链表,要求元素正确实现equals()和hashCode(),平均时间复杂度O(n)。

用 LinkedHashSet 保留顺序去重最常用
Java 中对 List 去重且保持原有顺序,LinkedHashSet 是最直接、安全的选择。它底层基于哈希表 + 双向链表,插入顺序可保证,同时自动过滤重复元素。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 构造时传入原
List:new LinkedHashSet(originalList),再转回ArrayList - 注意:要求元素正确实现
equals()和hashCode(),否则去重失效 - 不适用于含
null元素的自定义对象(除非你确认其equals处理了null) - 性能上,平均 O(n),比遍历 +
contains快得多
示例:
Listlist = Arrays.asList("a", "b", "a", "c"); List unique = new ArrayList<>(new LinkedHashSet<>(list)); // ["a", "b", "c"]
用 Stream.distinct() 在 Java 8+ 中函数式去重
distinct() 是 Stream 的中间操作,依赖元素的 equals() 判断是否重复,天然支持顺序保留(前提是源 List 是有序的)。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 必须配合
collect(Collectors.toList())才能得到新List - 对基本类型包装类、
String等默认实现没问题;自定义类务必重写equals和hashCode - 不能用于并行流(
parallelStream())去重——distinct在并行下不保证顺序,且可能失效 - 如果原列表很大,但只需去重后前 N 个,可用
limit(N)提前终止
示例:
Listnums = Arrays.asList(1, 2, 2, 3, 1); List unique = nums.stream().distinct().collect(Collectors.toList());
手动遍历 + contains() 去重——仅限小数据或教学场景
这是新手常写的写法,用一个新 List 存结果,遍历原列表,每次检查 !result.contains(item) 再添加。看似直观,实际有严重隐患。
常见错误现象:
- 时间复杂度 O(n²),10 万条数据可能卡死
- 若元素是自定义对象但没重写
equals,所有对象都被视为不同,去重完全失效 - 用
ArrayList做contains查找,底层是线性扫描,比HashSet慢几个数量级
除非调试验证逻辑,或处理几十个以内数据,否则不要在生产代码中这么写。
按特定字段去重(如对象的 id)需自定义逻辑
当 List 需按 userId 去重,而非整个对象相等时,LinkedHashSet 和 distinct() 都无法直接满足——它们依赖对象整体的 equals。
实操建议:
立即学习“Java免费学习笔记(深入)”;
- 用
Stream.collect()配合Collectors.toMap(),以字段为 key,保留第一个出现的对象:list.stream().collect(Collectors.toMap( User::getId, user -> user, (u1, u2) -> u1 // 冲突时保留前者 )).values().stream().collect(Collectors.toList()); - 或者用
Set手动遍历,检查seenIds = new HashSet() seenIds.add(user.getId())返回值 - 避免用
TreeSet或排序后再去重——会打乱原始顺序,且更慢
这种场景下,“去重依据”和“是否保留顺序”必须明确,否则容易漏掉关键业务语义。
真正麻烦的不是选哪个方法,而是没想清楚:元素是否可比较、是否允许修改原集合、是否要保留首次出现的位置、以及自定义对象的 equals 是否可信。这几个点没核对清楚,再“标准”的写法也会出错。










