Java 集合:LinkedHashSet 类

LinkedHashSet 是 Java 集合框架中 HashSet 的子类,它继承了 HashSet 的所有特性(如不允许重复元素、允许 null 值),并额外保证了元素的迭代顺序与插入顺序一致。其底层通过 LinkedHashMap 实现,兼顾了哈希表的高效性能和链表的顺序性。

  

LinkedHashSet 主要特性

  • 有序性:与 HashSet 的无序性不同,LinkedHashSet 会记录元素的插入顺序,迭代时按元素添加的先后顺序返回。这种顺序性是通过底层链表结构维护的。

  • 去重性:与 HashSet 一致,依赖元素的 hashCode() 和 equals() 方法判断重复,确保集合中无重复元素。

  • 允许 null 值:最多可存储 1 个 null 值(符合去重规则)。

  • 线程不安全:非线程安全集合,多线程并发修改可能导致 ConcurrentModificationException异常。

  • 性能:基本操作(add、remove、contains)的时间复杂度与 HashSet 接近(理想情况下 O (1)),但由于需要维护链表结构,性能略低于 HashSet。迭代操作的性能优于 HashSet,因为无需遍历整个哈希表,只需遍历链表(时间复杂度与元素数量成正比 O (n),但常数项更小)。

  

LinkedHashSet 简单示例

下面是一个 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 原理分析

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 文档。

  

说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
其他应用
公众号