应使用 LinkedHashSet 实现去重且保持插入顺序,它通过哈希表加双向链表实现 O(1) 操作与顺序维护;HashSet 无法保证插入顺序,不推荐强行配合外部列表模拟。

HashSet 本身不保证插入顺序,它基于哈希表实现,元素存储位置由 hash 值决定,遍历时顺序是不确定的。所以,单纯用 HashSet 无法同时做到去重 + 保持插入顺序。
用 LinkedHashSet 替代 HashSet
这是最直接、最推荐的方式。LinkedHashSet 是 HashSet 的子类,内部用哈希表 + 双向链表实现,既继承了 O(1) 查找/添加的去重能力,又通过链表维护了插入顺序。
- 声明和初始化方式与 HashSet 几乎一致:Set
set = new LinkedHashSet(); - 添加元素(自动去重,顺序不变):set.add("a"); set.add("b"); set.add("a"); // 结果为 ["a", "b"]
- 遍历时顺序就是插入顺序:for (String s : set) { ... } 输出 a → b
如果必须用 HashSet,再按插入顺序遍历?不行
不能。HashSet 没有记录插入顺序的机制,哪怕你用 ArrayList 记录过插入顺序,也无法让 HashSet “自己”按该顺序返回——因为它的 iterator 不承诺顺序,且每次迭代可能不同。
- 错误思路:先往 HashSet 加,再靠外部列表排序 → 这已脱离 HashSet 职责,不如直接用 LinkedHashSet
- 注意:HashSet 的迭代顺序在 JDK 版本或扩容后可能变化,不应依赖
其他可选方案(按需考虑)
多数场景 LinkedHashSet 已足够;特殊需求下可考虑:
- ArrayList + 手动查重:适合数据量极小、插入远多于遍历的场景,但 add() 变成 O(n),不推荐常规使用
- TreeSet:能排序(自然序或自定义),但不是按插入顺序,而是按元素大小顺序,且要求元素可比较
- 第三方库如 Guava 的 LinkedHashMultiset:支持重复计数但仍保持顺序,但普通去重用不到











