Java 集合--快速掌握涵盖三大场景实现的Set集合底层原理
掌握Map集合相当于同时掌握了Set集合。
Set集合底层实现--委派成员变量Map集合完成具体实现。
Set 集合概览
在 Java 集合框架中,Set 表示不包含重复元素的集合类型。
本文讲解三种常用实现:HashSet、LinkedHashSet 和 TreeSet。

1. Set 接口概述
定义:java.util.Set<E> 继承自 Collection<E>,用于存储无重复元素的集合。
核心特性:元素唯一性。
常见操作:
add(E e)、remove(Object o)、contains(Object o)- 批量操作:
addAll、removeAll、retainAll - 遍历:增强 for、迭代器
2. 基本实现对比
Set类的这三种实现类的实现逻辑,都是通过委派给内部的Map集合对象来实现具体处理逻辑,完全屏蔽了调用者对Set集合细节的感知。
类似于可重入锁,在 ReentrantLock(或 ReentrantReadWriteLock)中,所有对外的方法(lock()、unlock()……)都是简单地委派给内部的 Sync sync(FairSync 或 NonfairSync)来执行,完全屏蔽了调用者对 AQS 细节的感知。
HashSet、LinkedHashSet 和 TreeSet三者对比:
| Set类 | 底层结构 | 特性 | 允许 null | 迭代顺序 |
|---|---|---|---|---|
| HashSet | HashMap<E,Object> | 无序、高效 | 是 | 不确定(受 hash 冲突和容量影响) |
| LinkedHashSet | LinkedHashMap<E,Object> | 插入顺序(无访问顺序) | 是 | 插入顺序(无访问顺序) |
| TreeSet | TreeMap<E,Object> | 有序、排序、范围操作 | 否 | 元素排序顺序 |
2 HashSet 集合

2.1. 底层数据结构
底层维护一个 HashMap<E, Object>,实际的value值为静态常量 PRESENT,这样Set集合的所有key指向同一个静态常量PRESENT,避免浪费内存空间。
private transient HashMap<E,Object> map;
private static final Object PRESENT = new Object();
public HashSet() {
map = new HashMap<>();
}
public boolean add(E e) {
return map.put(e, PRESENT)==null;
}
HashSet委派成员变量HashMap 来完成底层实现,在整个类的实现中,只需要关注HashMap的key如何处理即可。
比如,Set集合迭代的过程只需要迭代key即可
public Iterator<E> iterator() {
return map.keySet().iterator();
}
2.2. 应用与注意
适用场景:快速去重、大量元素的快速查找。
注意点:集合元素的类需要实现hashCode和equals方法,这跟HashMap的key对象特性一致。
学习HashSet前,可以先掌握HashMap。往期文章可视化的讲过了HashMap集合。
3 LinkedHashSet 详解
LinkedHashSet 继承于HashSet,LinkedHashSet所有构造方法都是使用父类来完成对象创建,源码如下:
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
...
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
...
}
public class HashSet<E>
extends AbstractSet<E>
implements Set<E>, Cloneable, java.io.Serializable{
...
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}
...
}
细心的会发现,构成方法中有个参数boolean dummy,是没有被使用到的!!这很重要!!
然而LinkedHashSet 所有构造方法都是调用HashSet(int initialCapacity, float loadFactor, boolean dummy) 来完成对象实例化的,也就是说LinkedHashSet无法通过构造方法使用LinkedHashMap集合的访问顺序,也无法直接实现LRU缓存,因为默认accessOrder始终为false,并且也无法指定accessOrder为true。
public LinkedHashMap(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor);
accessOrder = false;
}
但是LinkedHashSet构造方法默认都给了个true,有点迷惑人的设计和默认值。尽管他在方法上做了说明:@param dummy被忽略(区分这一点)。
为什么不需要访问顺序特性?
Set集合并不需要这种“最近用过”的跟踪——它们只需要保证不重复,或者按插入顺序迭代就够了。并且Set集合没有提供get方法访问元素,不存咋访问这个概念,也就不需要访问顺序这个特性。Set集合不像List集合那样可以根据下标进行随机访问方法,只能通过遍历的方式进行节点访问。

3.1. 底层数据结构
LinkedHashSet本质基于 LinkedHashMap,内部每个节点有 before/after 双向链指针,维护插入顺序。
LinkedHashSet 的构造器只接受初始容量和加载因子,它内部使用的是默认的插入顺序的 LinkedHashMap,并不提供切换到访问顺序的选项,构造时不可指定 accessOrder 为 true。
每个节点的内部结构为:

3.2. 如何实现LinkedHashSet集合的LRU 缓存?
构造时不可指定 accessOrder 为 true,默认只有插入顺序。那只能“曲线救国”,使用Collections.newSetFromMap来创建Set集合。
如果需要“按访问顺序”并自动淘汰最老元素(LRU),可以基于 LinkedHashMap+Collections.newSetFromMap(...) 来实现一个带缓存特性的 Set。
以下是通过LinkedHashMap集合实现的自定义LRU 缓存:
// 按访问顺序
Set<String> lruSet = Collections.newSetFromMap(
new LinkedHashMap<String, Boolean>(16, 0.75f, true) { // accessOrder = true
@Override
protected boolean removeEldestEntry(Map.Entry<String, Boolean> eldest) {
return size() > 4;
}
}
);
lruSet.add("A");
lruSet.add("B");
lruSet.add("C");
lruSet.add("D");
lruSet.add("E"); // A 会被移除
System.out.println(lruSet); // 执行结果:[B, C, D, E]
3.3. 场景与性能
适用场景:需要既去重,又按插入顺序遍历
性能开销:比 HashSet 多维护链表指针,插入/删除略慢
注意点:
- 集合元素的类需要实现
hashCode和equals方法,这跟LinkedHashMap的key对象特性一致; - 构造时不可指定
accessOrder为true,默认只有插入顺序。
4 TreeSet 详解

4.1 底层结构与红黑树特性
TreeSet本质基于 TreeMap<E, Object>,红黑树保证插入/删除后的平衡性。
TreeSet的特性与红黑树特性一致,
每个红黑树节点的内部结构为:

4.2 排序与范围操作示例
TreeSet<String> tree = new TreeSet<>(Arrays.asList("C", "A", "B", "D"));
System.out.println(tree);// 自然排序:[A, B, C, D]
TreeSet<String> desc = new TreeSet<>(Comparator.reverseOrder());
desc.addAll(tree);
System.out.println(desc);// 反序:[D, C, B, A]
// 范围视图
SortedSet<String> range = tree.subSet("B", true, "D", false);
System.out.println(range);// 包含 B,不包含 D:结果 [B, C]
4.3 场景与注意
适用场景:需要有序集合、区间查询、按顺序访问元素。
注意点:
元素类型必须实现
Comparable或通过构造方法提供自定义比较器Comparator。不支持
null,否则抛NullPointerException。
4. 三者详细对比
下面从底层数据结构、迭代顺序、主要操作性能、内存开销、空元素支持、典型场景等维度,对比 HashSet、LinkedHashSet 和 TreeSet 三种常用 Set 实现:
| 特性 | HashSet | LinkedHashSet | TreeSet |
|---|---|---|---|
| 底层结构 | 哈希表(数组 + 链表/红黑树) | 哈希表 + 双向链表 | 红黑树(Self‑balancing BST) |
| 迭代顺序 | 无序 | 插入顺序 | 排序顺序(自然顺序或自定义 Comparator) |
| add / remove / contains | 平均 O(1),最坏 O(n) | 平均 O(1),最坏 O(n) | O(log n) |
| iteration(遍历) | O(n),顺序不确定 | O(n),按照插入顺序 | O(n),按照排序顺序 |
| 内存开销 | 最小(仅哈希桶 + 链表/树节点) | 略高(每个节点多维护前后指针) | 最高(树节点需维护父/左右子指针及颜色信息) |
| null 支持 | 支持一个 null |
支持一个 null |
不支持 null(会抛 NPE) |
| 线程安全 | 非线程安全 | 非线程安全 | 非线程安全 |
| 适用场景 | 需要最快速的无序去重 | 需要去重,并保持元素插入顺序 | 需要去重,并有序访问或范围查询(如子集、headSet、tailSet) |
| 访问顺序(LRU) | 不支持 | 不支持(只能保持插入顺序) | 不支持 |
| 子集/范围操作 | 不提供 | 不提供 | 支持 subSet、headSet、tailSet 等导航方法 |
总结
这三者同属 Set 家族,共享“无重复元素”、高效去重的核心特性,又各司其职、在“顺序”与“性能”上做出不同取舍。Set集合底层通过委派成员变量Map集合完成具体实现,但特性稍有差异,使用时需要注意。如果没有掌握Map集合的,建议先把Map集合的HashMap,LinkedHashMap和TreeMap都学习一遍,关于这块的知识,之前已经通过可视化的方式分享过,感兴趣的可以前往学习。
往期推荐
原创不易,觉得还不错的,三连支持:点赞、分享、推荐↓
Java 集合--快速掌握涵盖三大场景实现的Set集合底层原理的更多相关文章
- Java 集合快速失败异常
快速失败 在JDK中,查看集合有很多关于快速失败的描述: 注意,此实现不是同步的.如果多个线程同时访问一个哈希映射,而其中至少一个线程从结构上修改了该映射,则它必须 保持外部同步.(结构上的修改是指添 ...
- JAVA WEB快速入门之从编写一个基于SpringMVC框架的网站了解Maven、SpringMVC、SpringJDBC
接上篇<JAVA WEB快速入门之通过一个简单的Spring项目了解Spring的核心(AOP.IOC)>,了解了Spring的核心(AOP.IOC)后,我们再来学习与实践Maven.Sp ...
- Java开发快速上手
Java开发快速上手 前言 1.我的大学 2.对初学者的建议 3.大牛的三大特点 4.与他人的差距 第一章 了解Java开发语言 前言 基础常识 1.1 什么是Java 1.1.1 跨平台性 1.2 ...
- Java提高班(四)面试必备—你不知道的数据集合
导读:Map竟然不属于Java集合框架的子集?队列也和List一样属于集合的三大子集之一?更有队列的正确使用姿势,一起来看吧! Java中的集合通常指的是Collection下的三个集合框架List. ...
- 牛客网Java刷题知识点之Java 集合框架的构成、集合框架中的迭代器Iterator、集合框架中的集合接口Collection(List和Set)、集合框架中的Map集合
不多说,直接上干货! 集合框架中包含了大量集合接口.这些接口的实现类和操作它们的算法. 集合容器因为内部的数据结构不同,有多种具体容器. 不断的向上抽取,就形成了集合框架. Map是一次添加一对元素. ...
- Java API 快速速查宝典
Java API 快速速查宝典 作者:明日科技,陈丹丹,李银龙,王国辉 著 出版社:人民邮电出版社 出版时间:2012年5月 Java编程的最基本要素是方法.属性和事件,掌握这些要素,就掌握了解决实际 ...
- Java Web快速入门——全十讲
Java Web快速入门——全十讲 这是一次培训的讲义,就是我在给学生讲的过程中记录下来的,非常完整,原来发表在Blog上,我感觉这里的学生可能更需要. 内容比较长,你可以先收藏起来,慢慢看. 第一讲 ...
- JAVA WEB快速入门之从编写一个基于SpringBoot+Mybatis快速创建的REST API项目了解SpringBoot、SpringMVC REST API、Mybatis等相关知识
JAVA WEB快速入门系列之前的相关文章如下:(文章全部本人[梦在旅途原创],文中内容可能部份图片.代码参照网上资源) 第一篇:JAVA WEB快速入门之环境搭建 第二篇:JAVA WEB快速入门之 ...
- JAVA WEB快速入门之通过一个简单的Spring项目了解Spring的核心(AOP、IOC)
接上篇<JAVA WEB快速入门之从编写一个JSP WEB网站了解JSP WEB网站的基本结构.调试.部署>,通过一个简单的JSP WEB网站了解了JAVA WEB相关的知识,比如:Ser ...
- lr_场景设计之知识点-集合点、loadgenerator
1.controller原理 通过场景设计来模拟用户的真实操作并调用bugen中的脚本,再通过设置的压力机产生压力,在场景运行中实时监控用户的执行情况,tps,响应时间,吞吐量,服务器资源使用情况: ...
随机推荐
- AtCoder Beginner Contest 357-D
Problem For a positive integer \(N\), let \(V_N\) be the integer formed by concatenating \(N\) exact ...
- Linux下如何使用perf/gdb/pstack分析性能与问题排查
本文分享自天翼云开发者社区<Linux下如何使用perf/gdb/pstack分析性能与问题排查>,作者:5****m 在Linux系统中,perf.gdb和pstack是三个常用的性能分 ...
- 鸿蒙Next仓颉语言开发实战教程:订单详情
幽蓝君听说HarmonyOS 5.1版本即将推送,6.0版本也快要来了,表示十分期待. 今天继续分享仓颉语言开发商城应用的实战教程,今天要分享的是订单详情页: 我们今天应该是第一次遇到分为上中下三部分 ...
- 袋鼠云春季发布会圆满落幕,构建Data+AI新质生产力
4月10日,以"Data+AI,构建新质生产力"为主题的袋鼠云春季发布会圆满落幕.大会中,袋鼠云带来了一系列"+AI"的数字化产品与最新行业沉淀,旨在将数据与A ...
- 开源公开课丨大数据调度系统Taier任务调度介绍
一.直播介绍 前几期,我们为大家分享了Taier基本介绍.控制台.Web前端架构及数据开发介绍,本期我们为大家分享Taier任务调度介绍. 本次直播我们将从Taier的任务调度实例生成.调度及提交等方 ...
- jemelloc论文(中英翻译)
AScalable Concurrent malloc(3) Implementation for FreeBSD (基于FreeBSD的可伸缩的并发malloc(3)实现) 作者:Jason Eva ...
- 模块与包&相对绝对路径
[一]模块与包 (1)什么是模块 在Python中,一个py文件就是一个模块,文件名为xxx.py模块名则是xxx,导入模块可以引用模块中已经写好的功能. (2)模块的来源 内置的:python解释器 ...
- FastAPI中的敏感数据如何在不泄露的情况下翩翩起舞?
扫描二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长 发现1000+提升效率与开发的AI工具和实用程序:https://tools.cmdragon.cn/ 以下是关于FastAPI框架中敏 ...
- FastAPI安全加固:密钥轮换、限流策略与安全头部如何实现三重防护?
扫描二维码 关注或者微信搜一搜:编程智域 前端至全栈交流与成长 发现1000+提升效率与开发的AI工具和实用程序:https://tools.cmdragon.cn/ 一.密钥轮换自动化机制 实现方案 ...
- C#支持格式最多的解压缩开源库SharpCompress
string archivePath = "path/to/"; string extractPath = "path/to/extract/folder"; ...