推荐使用 LinkedHashSet 一步到位实现去重并保持插入顺序,其底层用哈希表支持 O(1) 查重、链表维护顺序;若需手动控制,可用 HashSet 辅助 ArrayList 遍历过滤,时间复杂度接近 O(n)。

HashSet 本身就能高效去重,但如果你需要保留插入顺序(ArrayList 的特性),又想利用 HashSet 的 O(1) 查重能力,常见做法是用 HashSet 辅助 ArrayList 去重——不是“结合”成一个结构,而是配合使用。
用 HashSet 记录已见元素,边遍历边过滤
这是最常用、最直观的方式:遍历原始 ArrayList,用 HashSet 快速判断是否已存在,只把新元素加入结果列表。
- 时间复杂度接近 O(n),比双重循环(O(n²))或 list.contains(O(n) 每次)快得多
- 空间上多用一个 HashSet,但换来的是速度提升
- 适合一次性去重,且需保持首次出现的顺序
List<String> original = Arrays.asList("a", "b", "a", "c", "b");
Set<String> seen = new HashSet<>();
List<String> unique = new ArrayList<>();
for (String item : original) {
if (seen.add(item)) { // add() 返回 true 表示之前不存在,同时插入成功
unique.add(item);
}
}
// unique = ["a", "b", "c"]
用 LinkedHashSet 一步到位(推荐)
如果目标只是「去重 + 保序」,LinkedHashSet 是更优解:它底层用哈希表实现 O(1) 查重,同时用链表维护插入顺序。
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- 一行构造即可:new ArrayList(new LinkedHashSet(original))
- 无需手动遍历,代码简洁,性能好,语义清晰
- 注意:原始 List 元素必须正确重写 equals() 和 hashCode()
List<String> original = Arrays.asList("x", "y", "x", "z");
List<String> unique = new ArrayList<>(new LinkedHashSet<>(original));
// unique = ["x", "y", "z"]
对自定义对象去重时的关键点
若 ArrayList 存的是 Person、Order 等自定义对象,仅靠 HashSet 或 LinkedHashSet 不够,必须确保:
立即学习“Java免费学习笔记(深入)”;
- equals() 和 hashCode() 正确重写,且逻辑一致(比如都基于 id 字段)
- 避免使用 IDE 自动生成但未校验的版本(例如只重写了 equals 却漏了 hashCode)
- 若对象不可变,建议将关键字段设为 final,减少意外
// 错误:只重写 equals,没重写 hashCode → HashSet 可能存重复对象
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Person person = (Person) o;
return id == person.id;
}
// 缺少对应的 hashCode() 实现!
不建议直接“混合” HashSet 和 ArrayList 的误区
有人试图用 ArrayList 存 HashSet,或让 HashSet 包含 ArrayList,这无法解决“去重并保序”的核心需求。
- HashSet 无序,不能替代 ArrayList 的顺序特性
- ArrayList 本身不提供高效查重,不能替代 HashSet 的哈希查找
- 真正有效的“结合”,是职责分离:HashSet 判重,ArrayList 存结果

















