Java 23 集合框架详解:Set 接口及实现类(HashSet、TreeSet、LinkedHashSet)
📚 Java 23 集合框架详解:Set
接口及实现类(HashSet
、TreeSet
、LinkedHashSet
)
📖 概述
Set
是 Java 集合框架中用于存储 无序、不重复元素 的接口。它的实现类包括 HashSet
、TreeSet
和 LinkedHashSet
,它们在底层数据结构、排序规则和性能特性上存在显著差异。
本文将详细介绍 Set
接口及其实现类 的使用案例、底层实现、性能优化方案、多线程优化及注意事项。
🏗️ 1. Set 接口的常用实现类
实现类 | 底层数据结构 | 排序方式 | 是否允许 null | 线程安全性 |
---|---|---|---|---|
HashSet | 哈希表 | 无序 | 允许 | 否 |
TreeSet | 红黑树 | 自然顺序/自定义排序 | 不允许 | 否 |
LinkedHashSet | 哈希表 + 双向链表 | 按插入顺序 | 允许 | 否 |
📋 2. HashSet
详解
✅ 2.1 特点
- 基于哈希表实现,元素按 哈希值 存储,存取效率高。
- 不保证元素顺序。
- 允许存储一个
null
值。 - 插入、删除、查找操作的平均时间复杂度为 O(1)。
🔧 2.2 使用案例
import java.util.HashSet;public class HashSetExample {public static void main(String[] args) {HashSet<String> set = new HashSet<>();set.add("Alice");set.add("Bob");set.add("Charlie");// 尝试添加重复元素set.add("Alice");// 遍历set.forEach(System.out::println);}
}
输出:
Alice
Bob
Charlie
🛠 2.3 优化方案
- 指定初始容量和负载因子:
HashSet<String> set = new HashSet<>(16, 0.75f);
- 避免频繁扩容,设置合适的初始容量,减少性能开销。
⚠️ 2.4 多线程优化
HashSet
是 线程不安全的,可以使用 Collections.synchronizedSet()
方法或 ConcurrentHashMap
来实现线程安全。
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;public class SynchronizedHashSetExample {public static void main(String[] args) {Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());synchronizedSet.add("Alice");synchronizedSet.add("Bob");synchronized (synchronizedSet) {synchronizedSet.forEach(System.out::println);}}
}
📋 3. TreeSet
详解
✅ 3.1 特点
- 基于红黑树实现,元素按 自然顺序 或 自定义顺序 排序。
- 不允许存储
null
值。 - 插入、删除、查找操作的时间复杂度为 O(log n)。
🔧 3.2 使用案例
import java.util.TreeSet;public class TreeSetExample {public static void main(String[] args) {TreeSet<Integer> set = new TreeSet<>();set.add(20);set.add(10);set.add(30);// 遍历set.forEach(System.out::println);}
}
输出:
10
20
30
🔧 使用自定义排序
import java.util.TreeSet;
import java.util.Comparator;public class CustomTreeSetExample {public static void main(String[] args) {TreeSet<String> set = new TreeSet<>(Comparator.reverseOrder());set.add("Alice");set.add("Bob");set.add("Charlie");set.forEach(System.out::println);}
}
输出:
Charlie
Bob
Alice
🛠 3.3 优化方案
- 避免使用
null
值,因为TreeSet
不允许存储null
。 - 选择合适的排序规则,根据业务需求使用 自然顺序 或 自定义排序。
⚠️ 3.4 多线程优化
TreeSet
是 线程不安全的,可以使用 Collections.synchronizedSet()
来实现线程安全。
import java.util.Collections;
import java.util.TreeSet;
import java.util.Set;public class SynchronizedTreeSetExample {public static void main(String[] args) {Set<Integer> synchronizedSet = Collections.synchronizedSet(new TreeSet<>());synchronizedSet.add(10);synchronizedSet.add(20);synchronized (synchronizedSet) {synchronizedSet.forEach(System.out::println);}}
}
📋 4. LinkedHashSet
详解
✅ 4.1 特点
- 基于哈希表和双向链表实现。
- 保留元素的插入顺序。
- 允许存储一个
null
值。
🔧 4.2 使用案例
import java.util.LinkedHashSet;public class LinkedHashSetExample {public static void main(String[] args) {LinkedHashSet<String> set = new LinkedHashSet<>();set.add("Alice");set.add("Bob");set.add("Charlie");// 遍历set.forEach(System.out::println);}
}
输出:
Alice
Bob
Charlie
🛠 4.3 优化方案
- 使用
LinkedHashSet
时,考虑内存占用问题,因为双向链表会占用更多内存。 - 适用于需要按插入顺序遍历的场景。
⚠️ 4.4 多线程优化
LinkedHashSet
是 线程不安全的,可以使用 Collections.synchronizedSet()
来实现线程安全。
import java.util.Collections;
import java.util.LinkedHashSet;
import java.util.Set;public class SynchronizedLinkedHashSetExample {public static void main(String[] args) {Set<String> synchronizedSet = Collections.synchronizedSet(new LinkedHashSet<>());synchronizedSet.add("Alice");synchronizedSet.add("Bob");synchronized (synchronizedSet) {synchronizedSet.forEach(System.out::println);}}
}
🔄 5. 三者对比总结
特性 | HashSet | TreeSet | LinkedHashSet |
---|---|---|---|
底层数据结构 | 哈希表 | 红黑树 | 哈希表 + 双向链表 |
是否允许 null | 是 | 否 | 是 |
排序方式 | 无序 | 自然顺序/自定义排序 | 插入顺序 |
插入/删除性能 | O(1) | O(log n) | O(1) |
适用场景 | 快速查找和去重 | 排序数据集 | 保留插入顺序 |
🎯 6. 选择指南
场景 | 推荐实现类 |
---|---|
快速查找和去重 | HashSet |
需要排序的集合 | TreeSet |
需要保留插入顺序的集合 | LinkedHashSet |
⚙️ 7. 总结
HashSet
适用于 快速查找和去重 的场景。TreeSet
适用于 需要排序的集合。LinkedHashSet
适用于 需要保留插入顺序的集合。