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


相关文章
|
14天前
|
存储 缓存 安全
Java 集合江湖:底层数据结构的大揭秘!
小米是一位热爱技术分享的程序员,本文详细解析了Java面试中常见的List、Set、Map的区别。不仅介绍了它们的基本特性和实现类,还深入探讨了各自的使用场景和面试技巧,帮助读者更好地理解和应对相关问题。
34 5
|
26天前
|
存储 缓存 安全
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
38 4
|
1月前
|
Java
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式
Java 8 引入的 Streams 功能强大,提供了一种简洁高效的处理数据集合的方式。本文介绍了 Streams 的基本概念和使用方法,包括创建 Streams、中间操作和终端操作,并通过多个案例详细解析了过滤、映射、归并、排序、分组和并行处理等操作,帮助读者更好地理解和掌握这一重要特性。
33 2
|
1月前
|
安全 Java
Java多线程集合类
本文介绍了Java中线程安全的问题及解决方案。通过示例代码展示了使用`CopyOnWriteArrayList`、`CopyOnWriteArraySet`和`ConcurrentHashMap`来解决多线程环境下集合操作的线程安全问题。这些类通过不同的机制确保了线程安全,提高了并发性能。
|
3月前
|
Java
安装JDK18没有JRE环境的解决办法
安装JDK18没有JRE环境的解决办法
381 3
|
3天前
|
NoSQL 关系型数据库 MySQL
Linux安装jdk、mysql、redis
Linux安装jdk、mysql、redis
58 7
|
4月前
|
Oracle Java 关系型数据库
Mac安装JDK1.8
Mac安装JDK1.8
784 4
|
4月前
|
Java 关系型数据库 MySQL
"解锁Java Web传奇之旅:从JDK1.8到Tomcat,再到MariaDB,一场跨越数据库的冒险安装盛宴,挑战你的技术极限!"
【8月更文挑战第19天】在Linux上搭建Java Web应用环境,需安装JDK 1.8、Tomcat及MariaDB。本指南详述了使用apt-get安装OpenJDK 1.8的方法,并验证其版本。接着下载与解压Tomcat至`/usr/local/`目录,并启动服务。最后,通过apt-get安装MariaDB,设置基本安全配置。完成这些步骤后,即可验证各组件的状态,为部署Java Web应用打下基础。
65 1
|
1月前
|
Oracle Java 关系型数据库
安装 JDK 时应该注意哪些问题
选择合适的JDK版本需考虑项目需求与兼容性,推荐使用LTS版本如JDK 17或21。安装时注意操作系统适配,配置环境变量PATH和JAVA_HOME,确保合法使用许可证,并进行安装后测试以验证JDK功能正常。
55 1
|
1月前
|
IDE Java 编译器
开发 Java 程序一定要安装 JDK 吗
开发Java程序通常需要安装JDK(Java Development Kit),因为它包含了编译、运行和调试Java程序所需的各种工具和环境。不过,某些集成开发环境(IDE)可能内置了JDK,或可使用在线Java编辑器,无需单独安装。
72 1