深度解析LinkedHashSet工作原理

分类: bat365app手机版下载 时间: 2025-09-15 22:00:35 作者: admin 阅读: 4757
深度解析LinkedHashSet工作原理

一、引言

在 Java 的集合框架中,LinkedHashSet 是一个独特且实用的类。它结合了 HashSet 的元素唯一性和链表的有序性,既能够确保存储的元素不重复,又能按照元素插入的顺序或者访问的顺序来维护元素。本文将深入探讨 LinkedHashSet 的原理,包括其底层数据结构、核心属性、构造方法、常用操作的实现细节,还会结合丰富的代码示例进行详细说明。

二、LinkedHashSet 概述

2.1 定义与用途

LinkedHashSet 是 Java 集合框架中 Set 接口的一个具体实现类,位于 java.util 包下。它继承自 HashSet,并额外维护了一个双向链表,用于记录元素的插入顺序或者访问顺序。这使得 LinkedHashSet 非常适合那些需要保持元素插入顺序,同时又要保证元素唯一性的场景,例如缓存、历史记录管理等。

2.2 继承关系与实现接口

LinkedHashSet 的继承关系如下:

java.lang.Object

└─ java.util.AbstractCollection

└─ java.util.AbstractSet

└─ java.util.HashSet

└─ java.util.LinkedHashSet

它实现了 Set、Cloneable 和 java.io.Serializable 接口,具备集合的基本操作,支持克隆和序列化。

import java.util.LinkedHashSet;

import java.util.Set;

public class LinkedHashSetOverview {

public static void main(String[] args) {

// 创建一个 LinkedHashSet 对象

LinkedHashSet linkedHashSet = new LinkedHashSet<>();

// 可以将其赋值给 Set 接口类型的变量

Set set = linkedHashSet;

}

}

三、底层数据结构:哈希表与双向链表

3.1 哈希表的作用

LinkedHashSet 基于 HashSet 实现,而 HashSet 底层是基于哈希表(实际上是 HashMap)。哈希表通过哈希函数将元素映射到一个固定大小的数组中的某个位置,这个位置称为桶(Bucket)。当多个元素映射到同一个桶时,就会发生哈希冲突。LinkedHashSet 使用链地址法来解决哈希冲突,即每个桶中存储一个链表,链表中的节点存储元素。

3.2 双向链表的作用

LinkedHashSet 额外维护了一个双向链表,链表中的节点不仅包含元素本身,还包含指向前一个节点和后一个节点的引用。这个双向链表按照元素的插入顺序或者访问顺序将所有元素连接起来,从而保证了元素的有序性。

3.3 节点结构

LinkedHashSet 中的节点是 LinkedHashMap.Entry 类型,它继承自 HashMap.Node,并额外添加了 before 和 after 两个引用,用于维护双向链表的顺序。

static class Entry extends HashMap.Node {

Entry before, after;

Entry(int hash, K key, V value, Node next) {

super(hash, key, value, next);

}

}

四、核心属性

LinkedHashSet 本身没有定义太多的核心属性,主要依赖于 HashSet 和 LinkedHashMap 的属性。其中,LinkedHashMap 是 LinkedHashSet 底层实际使用的数据结构,它有以下几个重要属性:

// 双向链表的头节点

transient LinkedHashMap.Entry head;

// 双向链表的尾节点

transient LinkedHashMap.Entry tail;

// 访问顺序标志,默认为 false,表示按照插入顺序

final boolean accessOrder;

head:指向双向链表的头节点,即最早插入的元素。tail:指向双向链表的尾节点,即最晚插入的元素。accessOrder:一个布尔值,用于控制双向链表的顺序。如果为 false,则按照元素的插入顺序;如果为 true,则按照元素的访问顺序。

五、构造方法

5.1 无参构造方法

public LinkedHashSet() {

super(16, .75f, true);

}

无参构造方法调用父类 HashSet 的构造方法,创建一个初始容量为 16,负载因子为 0.75 的 LinkedHashSet,并将 accessOrder 设置为 true,表示按照插入顺序维护元素。

5.2 指定初始容量的构造方法

public LinkedHashSet(int initialCapacity) {

super(initialCapacity, .75f, true);

}

该构造方法允许指定 LinkedHashSet 的初始容量,负载因子默认为 0.75,同样按照插入顺序维护元素。

5.3 指定初始容量和负载因子的构造方法

public LinkedHashSet(int initialCapacity, float loadFactor) {

super(initialCapacity, loadFactor, true);

}

此构造方法允许同时指定初始容量和负载因子,accessOrder 仍然为 true。

5.4 从其他集合创建 LinkedHashSet 的构造方法

public LinkedHashSet(Collection c) {

super(Math.max(2*c.size(), 11), .75f, true);

addAll(c);

}

该构造方法接受一个集合作为参数,将集合中的元素添加到新创建的 LinkedHashSet 中。初始容量根据传入集合的大小进行调整。

import java.util.ArrayList;

import java.util.Collection;

import java.util.LinkedHashSet;

import java.util.Set;

public class LinkedHashSetConstructors {

public static void main(String[] args) {

// 无参构造方法

LinkedHashSet linkedHashSet1 = new LinkedHashSet<>();

// 指定初始容量的构造方法

LinkedHashSet linkedHashSet2 = new LinkedHashSet<>(20);

// 指定初始容量和负载因子的构造方法

LinkedHashSet linkedHashSet3 = new LinkedHashSet<>(15, 0.8f);

// 从其他集合创建 LinkedHashSet 的构造方法

Collection collection = new ArrayList<>();

collection.add("apple");

collection.add("banana");

LinkedHashSet linkedHashSet4 = new LinkedHashSet<>(collection);

System.out.println(linkedHashSet4);

}

}

六、常用操作原理

6.1 添加元素

public boolean add(E e) {

return map.put(e, PRESENT)==null;

}

add 方法实际上是调用 LinkedHashMap 的 put 方法将元素作为键,PRESENT 作为值存储到 LinkedHashMap 中。在插入元素时,LinkedHashMap 会先计算元素的哈希值,找到对应的桶,然后遍历桶中的链表,检查元素是否已经存在。如果元素不存在,则创建一个新的 LinkedHashMap.Entry 节点,并将其插入到哈希表的桶中,同时将该节点添加到双向链表的尾部。

6.2 检查元素是否存在

public boolean contains(Object o) {

return map.containsKey(o);

}

contains 方法调用 LinkedHashMap 的 containsKey 方法来检查指定元素是否存在于 LinkedHashSet 中。它会根据元素的哈希值找到对应的桶,然后遍历桶中的链表进行查找。

6.3 删除元素

public boolean remove(Object o) {

return map.remove(o)==PRESENT;

}

remove 方法调用 LinkedHashMap 的 remove 方法来删除指定元素。在删除元素时,LinkedHashMap 会先找到该元素对应的节点,然后将其从哈希表的桶中移除,同时将该节点从双向链表中移除。

6.4 获取元素数量

public int size() {

return map.size();

}

size 方法调用 LinkedHashMap 的 size 方法返回 LinkedHashSet 中元素的数量。

import java.util.LinkedHashSet;

public class LinkedHashSetOperations {

public static void main(String[] args) {

LinkedHashSet linkedHashSet = new LinkedHashSet<>();

// 添加元素

linkedHashSet.add("apple");

linkedHashSet.add("banana");

linkedHashSet.add("cherry");

// 检查元素是否存在

System.out.println("Contains apple: " + linkedHashSet.contains("apple"));

// 删除元素

linkedHashSet.remove("banana");

System.out.println("After removing banana: " + linkedHashSet);

// 获取元素数量

System.out.println("Size: " + linkedHashSet.size());

}

}

七、迭代器原理

LinkedHashSet 的迭代器是基于双向链表实现的,因此迭代器会按照元素的插入顺序或者访问顺序依次访问元素。迭代器的实现相对简单,只需要从双向链表的头节点开始,依次访问每个节点即可。

import java.util.Iterator;

import java.util.LinkedHashSet;

public class LinkedHashSetIterator {

public static void main(String[] args) {

LinkedHashSet linkedHashSet = new LinkedHashSet<>();

linkedHashSet.add("apple");

linkedHashSet.add("banana");

linkedHashSet.add("cherry");

Iterator iterator = linkedHashSet.iterator();

while (iterator.hasNext()) {

System.out.println(iterator.next());

}

}

}

八、性能分析

8.1 时间复杂度

插入操作:插入操作的平均时间复杂度为 O(1),因为哈希表可以通过哈希函数快速定位到桶的位置,在没有哈希冲突的情况下,插入操作可以在常数时间内完成。但在极端情况下,当所有元素都映射到同一个桶时,插入操作的时间复杂度会退化为 O(n)。查找操作:查找操作的平均时间复杂度为 O(1),同样可以通过哈希函数快速定位到桶的位置,然后在桶中查找元素。删除操作:删除操作的平均时间复杂度为 O(1),通过哈希函数定位到桶的位置,然后在桶中删除元素,并从双向链表中移除该节点。

8.2 空间复杂度

LinkedHashSet 的空间复杂度为 O(n),主要用于存储哈希表和双向链表中的节点。

九、注意事项

9.1 元素的哈希码和相等性

LinkedHashSet 判断元素是否重复是基于元素的 hashCode() 和 equals() 方法。因此,存储在 LinkedHashSet 中的元素必须正确重写这两个方法,否则可能会导致元素重复存储或查找失败。

9.2 线程安全问题

LinkedHashSet 不是线程安全的。如果在多线程环境下需要使用线程安全的集合,可以考虑使用 Collections.synchronizedSet() 方法将 LinkedHashSet 包装成线程安全的集合。

import java.util.Collections;

import java.util.LinkedHashSet;

import java.util.Set;

public class LinkedHashSetThreadSafety {

public static void main(String[] args) {

LinkedHashSet linkedHashSet = new LinkedHashSet<>();

Set synchronizedSet = Collections.synchronizedSet(linkedHashSet);

}

}

十、总结

LinkedHashSet 是 Java 集合框架中一个非常实用的类,它结合了 HashSet 的元素唯一性和链表的有序性。通过哈希表和双向链表的组合,LinkedHashSet 能够高效地进行元素的插入、查找和删除操作,同时保持元素的插入顺序或访问顺序。在使用 LinkedHashSet 时,需要注意元素的哈希码和相等性的重写,以及线程安全问题。深入理解 LinkedHashSet 的原理和性能特点,有助于我们在实际开发中合理地使用它,提高程序的性能和可靠性。

相关文章

栖霞警方打击十字街“站街女”
surface官翻机(Surface官翻多久的保修)
oppo版本应用游戏中心app
EXO啵啵虎是谁(为什么边伯贤叫啵啵虎)