LinkedHashSet 是 Java 集合框架中 HashSet 的子类,它继承了 HashSet 的所有特性(如不允许重复元素、允许 null 值),并额外保证了元素的迭代顺序与插入顺序一致。其底层通过 LinkedHashMap 实现,兼顾了哈希表的高效性能和链表的顺序性。
有序性:与 HashSet 的无序性不同,LinkedHashSet 会记录元素的插入顺序,迭代时按元素添加的先后顺序返回。这种顺序性是通过底层链表结构维护的。
去重性:与 HashSet 一致,依赖元素的 hashCode() 和 equals() 方法判断重复,确保集合中无重复元素。
允许 null 值:最多可存储 1 个 null 值(符合去重规则)。
线程不安全:非线程安全集合,多线程并发修改可能导致 ConcurrentModificationException异常。
性能:基本操作(add、remove、contains)的时间复杂度与 HashSet 接近(理想情况下 O (1)),但由于需要维护链表结构,性能略低于 HashSet。迭代操作的性能优于 HashSet,因为无需遍历整个哈希表,只需遍历链表(时间复杂度与元素数量成正比 O (n),但常数项更小)。
下面是一个 LinkedHashSet 的简单示例,展示了其初始化、添加元素、删除元素、遍历等常用操作:
package com.hxstrive.java_collection.linkedHashSet;
import java.util.Iterator;
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetDemo {
public static void main(String[] args) {
// 1. 初始化 LinkedHashSet(无参构造)
Set<String> linkedHashSet = new LinkedHashSet<>();
// 2. 添加元素(add() 方法)
// 返回值为 boolean:添加成功返回 true,重复元素添加失败返回 false
boolean isAdded1 = linkedHashSet.add("张三");
boolean isAdded2 = linkedHashSet.add("李四");
boolean isAdded3 = linkedHashSet.add("王五");
boolean isAdded4 = linkedHashSet.add("张三"); // 添加重复元素
System.out.println("添加结果:" + isAdded1 + "," + isAdded2 + "," + isAdded3 + "," + isAdded4);
System.out.println("添加后集合元素(保持插入顺序):" + linkedHashSet);
// 3. 判断元素是否存在(contains() 方法)
boolean has1 = linkedHashSet.contains("李四");
boolean has2 = linkedHashSet.contains("赵六");
System.out.println("\n是否包含李四:" + has1);
System.out.println("是否包含赵六:" + has2);
// 4. 删除元素(remove() 方法)
boolean isRemoved = linkedHashSet.remove("李四");
System.out.println("\n删除李四的结果:" + isRemoved);
System.out.println("删除后集合元素:" + linkedHashSet);
// 5. 重新添加已删除的元素,观察顺序变化
linkedHashSet.add("李四");
System.out.println("\n重新添加李四后:" + linkedHashSet); // 新元素会放在末尾
// 6. 集合大小(size() 方法)
System.out.println("\n集合当前大小:" + linkedHashSet.size());
// 7. 遍历 LinkedHashSet(所有方式均保持插入顺序)
// 方式1:增强 for 循环
System.out.println("\n增强 for 循环遍历:");
for (String name : linkedHashSet) {
System.out.print(name + " ");
}
// 方式2:迭代器(Iterator)
System.out.println("\n\n迭代器遍历:");
Iterator<String> iterator = linkedHashSet.iterator();
while (iterator.hasNext()) {
String name = iterator.next();
System.out.print(name + " ");
}
// 方式3:forEach + Lambda 表达式(Java 8+)
System.out.println("\n\nLambda 表达式遍历:");
linkedHashSet.forEach(name -> System.out.print(name + " "));
// 8. 清空集合(clear() 方法)
linkedHashSet.clear();
System.out.println("\n\n清空集合后是否为空:" + linkedHashSet.isEmpty());
}
}运行结果:
添加结果:true,true,true,false 添加后集合元素(保持插入顺序):[张三, 李四, 王五] 是否包含李四:true 是否包含赵六:false 删除李四的结果:true 删除后集合元素:[张三, 王五] 重新添加李四后:[张三, 王五, 李四] 集合当前大小:3 增强 for 循环遍历: 张三 王五 李四 迭代器遍历: 张三 王五 李四 Lambda 表达式遍历: 张三 王五 李四 清空集合后是否为空:true Process finished with exit code 0
LinkedHashSet 继承自 HashSet,其核心是通过调用 HashSet 的一个特殊构造方法,将底层存储结构指定为 LinkedHashMap(而非 HashMap),下面是 HashSet 的一个特殊构造方法源码:
/**
* 构造一个新的、空的链接哈希集合。(这个包访问权限的构造方法仅被 LinkedHashSet 使用)
* 底层的 HashMap 实例实际上是一个具有指定初始容量和指定负载因子的 LinkedHashMap。
*
* 注:此构造方法是 HashSet 为子类 LinkedHashSet 专门提供的,
* 用于创建基于 LinkedHashMap的 集合实例,从而实现元素的插入顺序保持特性。
*
* @param initialCapacity 哈希映射的初始容量
* @param loadFactor 哈希映射的负载因子
* @param dummy 忽略此参数(仅用于区分此构造方法与其他具有int、float参数的构造方法)
* @throws IllegalArgumentException 如果初始容量小于0,或者负载因子为非正数(≤0)
*/
HashSet(int initialCapacity, float loadFactor, boolean dummy) {
// 初始化底层存储为 LinkedHashMap,而非默认的 HashMap
// 这是 LinkedHashSet 能够保持插入顺序的核心原因
map = new LinkedHashMap<>(initialCapacity, loadFactor);
}LinkedHashSet 的源码比较简单,提供了四个构造方法和一个 spliterator() 方法,构造方法如下:
public class LinkedHashSet<E>
extends HashSet<E>
implements Set<E>, Cloneable, java.io.Serializable {
@java.io.Serial
private static final long serialVersionUID = -2851667679971038690L;
/**
* 构造一个新的、空的 LinkedHashSet,指定初始容量和负载因子
* 底层通过 LinkedHashMap 实现,保证元素的插入顺序
*
* @param initialCapacity LinkedHashSet 的初始容量
* @param loadFactor LinkedHashSet 的负载因子
* @throws IllegalArgumentException 如果初始容量小于0,或负载因子为非正数(≤0)
*/
public LinkedHashSet(int initialCapacity, float loadFactor) {
super(initialCapacity, loadFactor, true);
}
/**
* 构造一个新的、空的 LinkedHashSet,指定初始容量,使用默认负载因子(0.75)
*
* @param initialCapacity LinkedHashSet 的初始容量
* @throws IllegalArgumentException 如果初始容量小于0
*/
public LinkedHashSet(int initialCapacity) {
super(initialCapacity, .75f, true);
}
/**
* 构造一个新的、空的LinkedHashSet,使用默认初始容量(16)和默认负载因子(0.75)
*/
public LinkedHashSet() {
super(16, .75f, true);
}
/**
* 构造一个新的 LinkedHashSet,包含指定集合中的所有元素
* 初始容量设置为足以容纳指定集合元素的大小,使用默认负载因子(0.75)
*
* @param c 包含要放入此集合的元素的集合
* @throws NullPointerException 如果指定的集合为null
*/
public LinkedHashSet(Collection<? extends E> c) {
// 计算初始容量:取"2*集合大小"和11中的较大值,确保有足够空间存储元素
// 调用父类构造方法,指定容量、默认负载因子0.75和LinkedHashMap标识
super(Math.max(2*c.size(), 11), .75f, true);
addAll(c);
}
// 省略 spliterator 方法
}spliterator() 方法用来创建 Spliterator 的实例,Spliterator(可分割迭代器)是 Java 8 引入的一个新接口,位于 java.util 包下,主要用于支持集合的并行遍历,是对传统 Iterator 的补充。它允许将集合分割成多个子部分进行并行处理,从而充分利用多核处理器的性能优势。
spliterator() 方法定义如下:
/**
* 创建一个延迟绑定且快速失败的 Spliterator,用于遍历集合中的元素
* Spliterator 支持并行遍历,是 Iterator 的补充。
*
* 此 Spliterator 会报告以下特性:
* (1)Spliterator.SIZED:已知大小(可通过size()获取)
* (2)Spliterator.DISTINCT:元素不重复(符合Set特性)
* (3)Spliterator.ORDERED:元素按插入顺序排列(是 LinkedHashSet 的核心特性)
*
* 此实现通过集合的迭代器(Iterator)来创建延迟绑定的拆分迭代器。
* 该拆分迭代器继承了集合迭代器的快速失败属性。
* 创建的拆分迭代器还会额外报告 Spliterator.SUBSIZED 特性。
*
* @return 用于遍历集合元素的 Spliterator
* @since 1.8
*/
@Override
public Spliterator<E> spliterator() {
// 创建 Spliterator,指定集合和特性(元素不重复 + 有序)
return Spliterators.spliterator(this, Spliterator.DISTINCT | Spliterator.ORDERED);
}更多信息请参考 https://docs.oracle.com/javase/8/docs/api/java/util/LinkedHashSet.html API 文档。