应使用 LinkedHashSet 保持插入顺序,它是 HashSet 的子类,通过哈希表加双向链表实现,遍历时按插入顺序输出,时间复杂度仍为平均 O(1)。

HashSet 本身不保证插入顺序,它基于哈希表实现,元素存储位置由 hashCode 决定,遍历时顺序是不确定的。所以,不能直接用 HashSet 实现按添加顺序排序(注意:“按添加顺序”不是“按大小排序”,而是“插入序”)。
想保持插入顺序?用 LinkedHashSet
Java 提供了 LinkedHashSet,它是 HashSet 的子类,内部用哈希表 + 双向链表维护元素,天然记录插入顺序:
- 添加元素时,既存入哈希表加速查找,又追加到链表尾部
- 遍历(如 for-each、iterator)时,按链表顺序访问,即插入顺序
- 时间复杂度和 HashSet 基本一致:add、contains、remove 平均 O(1)
示例:
Set<String> set = new LinkedHashSet<>();set.add("first");
set.add("second");
set.add("third");
System.out.println(set); // [first, second, third]
如果必须用 HashSet,怎么“模拟”插入顺序?
不推荐,但技术上可行:额外用一个 List 记录插入顺序,所有操作同步维护两个结构:
立即学习“Java免费学习笔记(深入)”;
在 Java 中初始化和管理阿里云 SDK客户端。包括单例模式、线程安全、endpoint 与 region 配置、VPC 终端节点、同步与异步等。
- add 时:先检查 HashSet 是否已存在,不存在则同时 add 到 HashSet 和 List
- 遍历时:遍历 List,用其元素去 HashSet 中查(确保仍是集合语义)
- remove 时:从 HashSet 和 List 中都移除(List.remove(Object) 是线性查找,性能差)
这种做法冗余、易错、性能差,仅适合极特殊场景,日常开发应避免。
注意区分:插入顺序 vs 自然/自定义排序
如果你实际想要的是“按字符串字典序”或“按数字大小”等排序,那应该用:
-
TreeSet:基于红黑树,支持自然序或 Comparator,但不记插入顺序 - 或先用
LinkedHashSet收集,再转为List后调用Collections.sort()
例如按字母排序输出:
List<String> list = new ArrayList<>(linkedSet);Collections.sort(list);
总结关键点
- HashSet → 无序,只重效率和唯一性
- LinkedHashSet → 有序(插入序),兼顾效率和顺序,首选方案
- TreeSet → 有序(比较序),牺牲插入顺序换排序能力
- 手动同步 HashSet + List → 不必要,别这么干
不复杂但容易忽略:选对集合类型,比后期补救更简单可靠。

















