LinkedHashSet 能保持插入顺序是因为底层用 LinkedHashMap 实现,其双向链表按插入顺序链接节点;它不支持 null 元素,非线程安全,时间复杂度 O(1),内存开销略高于 HashSet。

LinkedHashSet 保留插入顺序,但不是线程安全的,也不能存 null(除非你用的是 Java 8+ 的 removeIf 等操作间接触发,但本身仍不支持 null 元素)。
为什么 LinkedHashSet 能保持插入顺序?
它底层用 LinkedHashMap 实现 —— 每个元素作为 key 存进 LinkedHashMap,value 固定为 PRESENT(一个静态 Object)。而 LinkedHashMap 维护了一个双向链表,节点按插入顺序链接,所以迭代时自然有序。
这和 TreeSet 的“排序”完全不同:TreeSet 是按 compareTo 或 Comparator 排序,LinkedHashSet 只认“谁先来”,不比较大小。
- 插入、删除、查找平均时间复杂度都是 O(1),和
HashSet一致 - 内存开销略高:每个节点多两个指针(前驱/后继),比
HashSet多约 8–16 字节/元素 - 初始化时建议指定初始容量,否则扩容时链表重哈希会打乱“逻辑插入顺序”的局部性(虽然语义上仍正确)
LinkedHashSet 和 TreeSet 在什么场景下别选错?
如果你需要“按添加顺序遍历”,比如缓存最近访问项、记录用户操作流水、构建配置项加载顺序,就用 LinkedHashSet;如果你要“自动升序/自定义序”,比如排行榜、区间查询、去重并排序输出,就该用 TreeSet。
立即学习“Java免费学习笔记(深入)”;
常见误用:
- 想按时间排序却用了
LinkedHashSet→ 实际只是按add()调用顺序,和系统时间无关 - 以为
LinkedHashSet支持重复元素的“多次插入” → 它仍是Set,重复add不生效,也不会更新位置 - 在多线程环境直接共享实例 →
LinkedHashSet非线程安全,ConcurrentHashMap+KeySetView或Collections.synchronizedSet()才是替代方案
构造 LinkedHashSet 时传入 Collection 会发生什么?
调用 new LinkedHashSet(Collection) 时,会按该集合的 iterator() 顺序逐个 add。这意味着:
- 如果传入的是
ArrayList,顺序就是列表索引顺序 - 如果传入的是
HashSet,顺序是不确定的(取决于其内部桶分布和哈希值) - 如果传入的是另一个
LinkedHashSet,顺序会被完整保留 - 传入
null会抛NullPointerException;传入含null元素的集合(如Arrays.asList(null))会在add时立即抛异常
所以别指望靠“用 HashSet 初始化 LinkedHashSet”来“去重并顺便排个序”——它只会把 HashSet 那套不可预测的迭代顺序固化下来。
最易被忽略的一点:它的序列化行为和 HashSet 不同,会把链表结构也写进去,反序列化后顺序严格还原;但如果你用 writeObject + 自定义序列化逻辑绕过默认机制,就可能破坏这个保证。










