LinkedHashSet能按插入顺序遍历,因其内部复用LinkedHashMap结构:哈希表判重,双向链表维护插入顺序;迭代时仅遍历链表,故顺序严格等于插入先后。

LinkedHashSet 为什么能按插入顺序遍历
因为它内部复用了 LinkedHashMap 的结构:哈希表负责判重(hashCode() + equals()),双向链表(每个节点含 before 和 after 引用)负责把每次成功 add() 的元素串在末尾。迭代时,集合不看哈希桶位置,只顺着链表从头走到尾——所以顺序严格等于插入先后。
- 重复元素调用
add()时,哈希表发现已存在 → 跳过链表插入,也不报错 -
null允许存一个,它的hashCode()是 0,同样参与链表维护 - 和
LinkedHashMap不同,它不支持访问顺序(比如 LRU),构造函数没有accessOrder参数
什么时候该用 LinkedHashSet,而不是 HashSet 或 TreeSet
直接看需求关键词:「去重」+「谁先加谁在前」→ 选 LinkedHashSet;只要「快去重」→ 用 HashSet;需要「自然序/自定义排序」→ 用 TreeSet。
- 读取配置文件行,去重后仍要保持原始声明顺序
- 用户点击日志流中过滤连续重复操作,但时间线不能乱
- 替代手写
ArrayList+contains()循环去重(后者是 O(n²),LinkedHashSet.add()是均摊 O(1)) - 别用它做排序用途:哪怕你传了
Comparator,它也不会生效——LinkedHashSet不接受比较器构造函数
多线程下怎么安全使用 LinkedHashSet
它本身是线程不安全的:并发 add() 可能导致链表断裂、迭代器抛 ConcurrentModificationException,甚至静默数据丢失。
- 最简单方案:
Collections.synchronizedSet(new LinkedHashSet()) - 高并发写场景,考虑用
CopyOnWriteArraySet(但注意它迭代快、写慢,且不保证插入顺序) - 若需顺序 + 线程安全 + 高性能,可封装成带锁的包装类,或改用
ConcurrentLinkedQueue+ 手动去重逻辑(代价是查重变慢) - 千万别在遍历中调用
remove()或add(),会触发快速失败机制
自定义对象放进 LinkedHashSet 容易踩的坑
只要这个对象的 hashCode() 和 equals() 行为稳定,就没问题;一旦修改了影响这两个方法的字段,集合就“认不出”它了。
立即学习“Java免费学习笔记(深入)”;
- 例如:把
User对象加进集合后,又改了它的id字段(而hashCode()基于id计算)→ 后续contains()返回false,remove()失效 - 链表结构本身不会坏,但哈希表索引失效,导致查找路径断开
- 建议:放进集合的对象尽量不可变(
final字段 + 无 setter),或确保关键字段不被修改
真正要注意的不是“怎么写”,而是“对象状态是否可控”——顺序只是链表的事,唯一性却牢牢绑在 hashCode()/equals() 的契约上。










