LinkedHashSet兼顾去重与插入顺序:基于哈希表判重、双向链表维护添加顺序,遍历时严格按插入先后返回;支持null,不支持排序或访问顺序,线程不安全。

解决去重与顺序兼顾的刚需
HashSet能高效去重,但遍历顺序不可控;TreeSet能排序,却强制按自然序或比较器重排。LinkedHashSet正好填补这个空缺:它既像HashSet一样靠哈希表保证唯一性,又用双向链表把每次添加的元素按顺序串起来。结果就是——插入时自动去重,遍历时严格按添加先后返回。
底层怎么做到“插得进、排得准、找得快”
它其实复用了LinkedHashMap的底层结构:哈希表负责快速定位和判重(基于hashCode()和equals()),双向链表(带before和after引用)则在每次add()成功后,把新节点追加到链表尾部。这样,哪怕哈希桶位置是散乱的,迭代器只需顺着链表从头走到尾,就能还原出原始插入序列。
- 添加重复元素时,哈希表判定已存在 → 不插入链表,也不报错
- 允许一个
null值,其hashCode()为0,同样参与链表维护 - 不支持访问顺序(如LRU),只认插入顺序 —— 这点和
LinkedHashMap的构造参数不同
哪些场景下它是最优解
只要需求同时满足“不能有重复”和“谁先来就谁在前”,LinkedHashSet基本就是开箱即用的选择:
- 解析配置文件行,去重后仍要保持原始配置项顺序
- 记录用户操作流水,过滤掉连续重复点击,但时间先后必须清晰
- 构建轻量级事件缓冲区,接收消息并去重,同时按到达顺序分发
- 替代手动用
ArrayList逐个检查再添加的低效去重逻辑
使用时需留意的关键细节
它不是万能银弹,几个实际开发中容易踩的点得提前知道:
立即学习“Java免费学习笔记(深入)”;
-
线程不安全:多线程写入需包装成
Collections.synchronizedSet()或加锁 - 性能略低于HashSet:多维护链表节点,内存稍高、插入稍慢,但日常业务几乎无感
-
对象状态别乱改:如果往集合里放的是自定义对象,后续修改了影响
hashCode()或equals()的字段,可能导致查不到、删不掉甚至破坏链表结构 -
不支持排序:它不会对字符串按字典序排,也不会对数字从小到大排,只忠于你调用
add()的那一瞬间的顺序










