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);
}


相关文章
|
3天前
|
监控 Java 应用服务中间件
高级java面试---spring.factories文件的解析源码API机制
【11月更文挑战第20天】Spring Boot是一个用于快速构建基于Spring框架的应用程序的开源框架。它通过自动配置、起步依赖和内嵌服务器等特性,极大地简化了Spring应用的开发和部署过程。本文将深入探讨Spring Boot的背景历史、业务场景、功能点以及底层原理,并通过Java代码手写模拟Spring Boot的启动过程,特别是spring.factories文件的解析源码API机制。
15 2
|
7天前
|
人工智能 监控 数据可视化
Java智慧工地信息管理平台源码 智慧工地信息化解决方案SaaS源码 支持二次开发
智慧工地系统是依托物联网、互联网、AI、可视化建立的大数据管理平台,是一种全新的管理模式,能够实现劳务管理、安全施工、绿色施工的智能化和互联网化。围绕施工现场管理的人、机、料、法、环五大维度,以及施工过程管理的进度、质量、安全三大体系为基础应用,实现全面高效的工程管理需求,满足工地多角色、多视角的有效监管,实现工程建设管理的降本增效,为监管平台提供数据支撑。
24 3
|
8天前
|
Java 数据处理 API
JDK 21中的序列集合:有序数据处理的新篇章
JDK 21引入了序列集合(Sequenced Collections),这是一种维护元素插入顺序的新型集合。本文介绍了序列集合的概念、特性及其应用场景,如事件日志记录、任务调度和数据处理。通过保持插入顺序和高效的遍历方法,序列集合为开发者提供了更直观和易用的API。
|
12天前
|
运维 自然语言处理 供应链
Java云HIS医院管理系统源码 病案管理、医保业务、门诊、住院、电子病历编辑器
通过门诊的申请,或者直接住院登记,通过”护士工作站“分配患者,完成后,进入医生患者列表,医生对应开具”长期医嘱“和”临时医嘱“,并在电子病历中,记录病情。病人出院时,停止长期医嘱,开具出院医嘱。进入出院审核,审核医嘱与住院通过后,病人结清缴费,完成出院。
42 3
|
18天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
21天前
|
移动开发 前端开发 JavaScript
java家政系统成品源码的关键特点和技术应用
家政系统成品源码是已开发完成的家政服务管理软件,支持用户注册、登录、管理个人资料,家政人员信息管理,服务项目分类,订单与预约管理,支付集成,评价与反馈,地图定位等功能。适用于各种规模的家政服务公司,采用uniapp、SpringBoot、MySQL等技术栈,确保高效管理和优质用户体验。
|
存储 SQL 关系型数据库
最新精心整理Java面试题,实现原理分析
最新精心整理Java面试题,实现原理分析
|
8天前
|
安全 Java 测试技术
Java并行流陷阱:为什么指定线程池可能是个坏主意
本文探讨了Java并行流的使用陷阱,尤其是指定线程池的问题。文章分析了并行流的设计思想,指出了指定线程池的弊端,并提供了使用CompletableFuture等替代方案。同时,介绍了Parallel Collector库在处理阻塞任务时的优势和特点。
|
4天前
|
安全 Java 开发者
深入解读JAVA多线程:wait()、notify()、notifyAll()的奥秘
在Java多线程编程中,`wait()`、`notify()`和`notifyAll()`方法是实现线程间通信和同步的关键机制。这些方法定义在`java.lang.Object`类中,每个Java对象都可以作为线程间通信的媒介。本文将详细解析这三个方法的使用方法和最佳实践,帮助开发者更高效地进行多线程编程。 示例代码展示了如何在同步方法中使用这些方法,确保线程安全和高效的通信。
23 9
|
7天前
|
存储 安全 Java
Java多线程编程的艺术:从基础到实践####
本文深入探讨了Java多线程编程的核心概念、应用场景及其实现方式,旨在帮助开发者理解并掌握多线程编程的基本技能。文章首先概述了多线程的重要性和常见挑战,随后详细介绍了Java中创建和管理线程的两种主要方式:继承Thread类与实现Runnable接口。通过实例代码,本文展示了如何正确启动、运行及同步线程,以及如何处理线程间的通信与协作问题。最后,文章总结了多线程编程的最佳实践,为读者在实际项目中应用多线程技术提供了宝贵的参考。 ####