Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理

简介: Java集合源码剖析——基于JDK1.8中HashSet、LinkedHashSet的实现原理

文章目录:


1.开篇

2.HashSet中的属性

3.HashSet中的方法

3.1 构造方法一

3.2 构造方法二

3.3 构造方法三

3.4 构造方法四

3.5 构造方法五

3.6 迭代器Iterator方法

3.7 size方法

3.8 isEmpty方法

3.9 contains方法

3.10 add方法

3.11 remove方法

3.12 clear方法

4.LinkedHashSet中的方法



1.开篇


前面三篇文章分别说到了 List 接口中的常用几个实现类:ArrayListLinkedListVector


Java集合体系中,List继承了Collection接口,Collection接口又继承了Iterable接口,而在Collection接口的主要的子接口中还有一个兄弟:Set


这篇文章主要来讲讲Set接口的主要实现类 HashSetLinkedHashSet的实现原理。那么我们看过部分源码的都知道Set集合的底层实际上就是相应的Map,所以说Set还不如说Map呢,所以这篇文章就简单的对Set集合的源码进行一个分析吧。

2.HashSet中的属性


·       HashSet的底层其实就是HashMap(哈希表数据结构),new HashSet的时候实际上就是new了一个HashMap

·       HashSet集合元素的特点:无序、不可重复、没有下标。

·       HashSet集合的默认初始化容量是16,默认加载因子是 .75。(HashMap

//底层使用 HashMap 来保存 HashSet 中所有元素
private transient HashMap<E,Object> map;
//定义一个虚拟的 Object 对象作为 HashMap 的 value,将此对象定义为 static final
private static final Object PRESENT = new Object();

3.HashSet中的方法


3.1 构造方法

//默认的无参构造器,构造一个空的 HashSet
//实际底层会初始化一个空的 HashMap,并使用默认初始容量为 16 和加载因子 0.75
public HashSet() {
    map = new HashMap<>();
}


3.2 构造方法二

//构造一个包含指定 collection 中的元素的新 set
//实际底层使用默认的加载因子 0.75 和足以包含指定 collection 中所有元素的初始容量来创建一个 HashMap
//c 其中的元素将存放在此 set 中的 collection
public HashSet(Collection<? extends E> c) {
    map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
    addAll(c);
}


3.3 构造方法三

//以指定的 initialCapacity 和 loadFactor 构造一个空的 HashSet
//实际底层以相应的参数构造一个空的 HashMap
//initialCapacity 初始容量,loadFactor 加载因子
public HashSet(int initialCapacity, float loadFactor) {
    map = new HashMap<>(initialCapacity, loadFactor);
}


3.4 构造方法四

//以指定的 initialCapacity 构造一个空的 HashSet
//实际底层以相应的参数及加载因子 loadFactor 为 0.75 构造一个空的 HashMap
public HashSet(int initialCapacity) {
    map = new HashMap<>(initialCapacity);
}


3.5 构造方法五

//以指定的 initialCapacity 和 loadFactor 构造一个新的空链接哈希集合
//此构造函数为包访问权限,不对外公开,实际只是是对 LinkedHashSet 的支持
//实际底层会以指定的参数构造一个空 LinkedHashMap 实例来实现
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
    map = new LinkedHashMap<>(initialCapacity, loadFactor);
}


3.6 迭代器Iterator方法

//返回对此 set 中元素进行迭代的迭代器。返回元素的顺序并不是特定的。
//底层实际调用底层 HashMap 的 keySet 来返回所有的 key
//可见 HashSet 中的元素,只是存放在了底层 HashMap 的 key 上,
//value 使用一个 static final 的 Object 对象标识
public Iterator<E> iterator() {
    return map.keySet().iterator();
}


3.7 size方法

//返回此 set 中的元素的数量(set 的容量)
//底层实际调用 HashMap 的 size()方法返回 Entry 的数量,就得到该 Set 中元素的个数
public int size() {
    return map.size();
}


3.8 isEmpty方法

//如果此 set 不包含任何元素,则返回 true
//底层实际调用 HashMap 的 isEmpty()判断该 HashSet 是否为空
public boolean isEmpty() {
    return map.isEmpty();
}


3.9 contains方法

//如果此 set 包含指定元素,则返回 true
//更确切地讲,当且仅当此 set 包含一个满足(o==null ? e==null : o.equals(e))的 e 元素时,返回 true
//底层实际调用 HashMap 的 containsKey 判断是否包含指定 key
public boolean contains(Object o) {
    return map.containsKey(o);
}


3.10 add方法

//如果此 set 中尚未包含指定元素,则添加指定元素
//更确切地讲,如果此 set 没有包含满足(e==null ? e2==null : e.equals(e2))的元素 e2,则向此 set 添加指定的元素 e
//如果此 set 已包含该元素,则该调用不更改 set 并返回 false
//底层实际将将该元素作为 key 放入 HashMap。
//由于 HashMap 的 put()方法添加 key-value 对时,当新放入 HashMap 的 Entry 中 key 与集合中原有 Entry 的 key 相同(hashCode()返回值相等,通过 equals 比较也返回 true)
//新添加的 Entry 的 value 会将覆盖原来 Entry 的 value,但 key 不会有任何改变,
//因此如果向 HashSet 中添加一个已经存在的元素时,新添加的集合元素将不会被放入 HashMap 中,原来的元素也不会有任何改变,这也就满足了 Set 中元素不重复的特性
public boolean add(E e) {
    return map.put(e, PRESENT)==null;
}


3.11 remove方法

//如果指定元素存在于此 set 中,则将其移除
//更确切地讲,如果此 set 包含一个满足(o==null ? e==null : o.equals(e))的元素e,则将其移除。如果此 set 已包含该元素,则返回 true
//(或者:如果此 set 因调用而发生更改,则返回 true)。(一旦调用返回,则此 set 不再包含该元素)。
//底层实际调用 HashMap 的 remove 方法删除指定 Entry
public boolean remove(Object o) {
    return map.remove(o)==PRESENT;
}


3.12 clear方法

//从此 set 中移除所有元素。此调用返回后,该 set 将为空
//底层实际调用 HashMap 的 clear 方法清空 Entry 中所有元素
public void clear() {
    map.clear();
}


4.LinkedHashSet中的方法


LinkedHashSet 是 HashSet 的一个子类。LinkedHashSet 是具有可预知迭代顺序的 Set 接口的哈希表和链接列表实现。此实现与HashSet 的不同之处在于,后者维护着一个运行于所有条目的双重链接列表。此链接列表定义了迭代顺序,该迭代顺序可为插入顺序或是访问顺序。


注意,此实现不是同步的。如果多个线程同时访问链接的哈希 Set,而其中至少一个线程修改了该 Set,则它必须保持外部同步。


对于 LinkedHashSet 而言,它继承与 HashSet、又基于 LinkedHashMap 来实现的。LinkedHashSet 底层使用 LinkedHashMap 来保存所有元素,它继承与 HashSet,其所有的方法操作上又与 HashSet 相同,因此 LinkedHashSet 的实现上非常简单,只提供了四个构造方法,并通过传递一个标识参数dummy,调用父类的构造器,底层构造一个 LinkedHashMap来实现,在相关操作上与父类 HashSet 的操作相同,直接调用父类 HashSet 的方法即可。


//构造一个带有指定初始容量和加载因子的新空链接哈希 set
//底层会调用父类的构造方法,构造一个有指定初始容量和加载因子的 LinkedHashMap 实例
initialCapacity 初始容量,loadFactor 加载因子
public LinkedHashSet(int initialCapacity, float loadFactor) {
    super(initialCapacity, loadFactor, true);
}
//构造一个带指定初始容量和默认加载因子 0.75 的新空链接哈希 set
//底层会调用父类的构造方法,构造一个带指定初始容量和默认加载因子 0.75 的 LinkedHashMap 实例
public LinkedHashSet(int initialCapacity) {
    super(initialCapacity, .75f, true);
}
//构造一个带默认初始容量 16 和加载因子 0.75 的新空链接哈希 set
//底层会调用父类的构造方法,构造一个带默认初始容量 16 和加载因子 0.75 的 LinkedHashMap 实例
public LinkedHashSet() {
    super(16, .75f, true);
}
//构造一个与指定 collection 中的元素相同的新链接哈希 set
//底层会调用父类的构造方法,构造一个足以包含指定 collection中所有元素的初始容量和加载因子为 0.75 的 LinkedHashMap 实例
//c 其中的元素将存放在此 set 中的 collection
public LinkedHashSet(Collection<? extends E> c) {
    super(Math.max(2*c.size(), 11), .75f, true);
    addAll(c);
}


目录
打赏
0
0
0
0
85
分享
相关文章
Java HashMap详解及实现原理
Java HashMap是Java集合框架中常用的Map接口实现,基于哈希表结构,允许null键和值,提供高效的存取操作。它通过哈希函数将键映射到数组索引,并使用链表或红黑树解决哈希冲突。HashMap非线程安全,多线程环境下需注意并发问题,常用解决方案包括ConcurrentHashMap和Collections.synchronizedMap()。此外,合理设置初始化容量和加载因子、重写hashCode()和equals()方法有助于提高性能和避免哈希冲突。
76 17
Java HashMap详解及实现原理
[Java计算机毕设]基于ssm的OA办公管理系统的设计与实现,附源码+数据库+论文+开题,包安装调试
OA办公管理系统是一款基于Java和SSM框架开发的B/S架构应用,适用于Windows系统。项目包含管理员、项目管理人员和普通用户三种角色,分别负责系统管理、请假审批、图书借阅等日常办公事务。系统使用Vue、HTML、JavaScript、CSS和LayUI构建前端,后端采用SSM框架,数据库为MySQL,共24张表。提供完整演示视频和详细文档截图,支持远程安装调试,确保顺利运行。
69 17
|
1月前
|
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
102 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
智慧产科一体化管理平台源码,基于Java,Vue,ElementUI技术开发,二开快捷
智慧产科一体化管理平台覆盖从备孕到产后42天的全流程管理,构建科室协同、医患沟通及智能设备互联平台。通过移动端扫码建卡、自助报道、智能采集数据等手段优化就诊流程,提升孕妇就诊体验,并实现高危孕产妇五色管理和孕妇学校三位一体化管理,全面提升妇幼健康宣教质量。
60 12
干货含源码!如何用Java后端操作Docker(命令行篇)
只有锻炼思维才能可持续地解决问题,只有思维才是真正值得学习和分享的核心要素。如果这篇博客能给您带来一点帮助,麻烦您点个赞支持一下,还可以收藏起来以备不时之需,有疑问和错误欢迎在评论区指出~
Java智慧工地(源码):数字化管理提升施工安全与质量
随着科技的发展,智慧工地已成为建筑行业转型升级的重要手段。依托智能感知设备和云物互联技术,智慧工地为工程管理带来了革命性的变革,实现了项目管理的简单化、远程化和智能化。
57 5
SaaS云计算技术的智慧工地源码,基于Java+Spring Cloud框架开发
智慧工地源码基于微服务+Java+Spring Cloud +UniApp +MySql架构,利用传感器、监控摄像头、AI、大数据等技术,实现施工现场的实时监测、数据分析与智能决策。平台涵盖人员、车辆、视频监控、施工质量、设备、环境和能耗管理七大维度,提供可视化管理、智能化报警、移动智能办公及分布计算存储等功能,全面提升工地的安全性、效率和质量。
探索Java动态代理的奥秘:JDK vs CGLIB
动态代理是一种在 运行时动态生成代理类的技术,无需手动编写代理类代码。它通过拦截目标方法的调用,实现对核心逻辑的 无侵入式增强(如日志、事务、权限控制等)。
62 0
探索Java动态代理的奥秘:JDK vs CGLIB
基于Java+SpringBoot+Vue实现的车辆充电桩系统设计与实现(系统源码+文档+部署讲解等)
面向大学生毕业选题、开题、任务书、程序设计开发、论文辅导提供一站式服务。主要服务:程序设计开发、代码修改、成品部署、支持定制、论文辅导,助力毕设!
深入理解 Java JDK —— 让我们从基础到进阶
JDK(Java Development Kit)是 Java 开发的核心工具包,包含编译、运行和调试 Java 程序所需的所有工具和库。它主要由 JVM(Java 虚拟机)、JRE(Java 运行时环境)和 Java 核心类库组成。JVM 是跨平台运行的基础,负责字节码的加载、执行和内存管理;JRE 提供运行 Java 应用的环境;核心类库则提供了丰富的 API 支持。通过编写、编译和运行一个简单的 Java 程序,可以深入理解 JDK 的工作原理。此外,JDK 还提供了 JIT 编译、垃圾回收优化和并发工具包等高级功能,帮助开发者提高程序性能和稳定性。
185 10
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等