Set接口概述
Set是Java集合框架中不允许重复元素的集合,不保证元素顺序
核心特征
Set接口继承自Collection接口,具有以下核心特性:
不允许重复元素
Set中不能包含重复的元素,每个元素都是唯一的
不保证顺序
Set不保证维护元素的插入顺序(LinkedHashSet除外)
元素判等机制
基于equals()和hashCode()方法判断元素是否重复
重要提示
存储在Set中的对象必须正确实现equals()和hashCode()方法,否则可能导致不可预期的行为。
HashSet详解
基于哈希表的Set实现,提供最佳查找性能
核心特性
- 基于HashMap实现,使用哈希表存储数据
- 不保证元素的顺序(无序集合)
- 允许null元素
- 添加、删除和查找操作的时间复杂度为O(1)
1
创建HashSet
import java.util.HashSet;
import java.util.Set;
// 创建HashSet实例
Set<String> hashSet = new HashSet<>();
// 创建带有初始容量的HashSet
Set<Integer> numbers = new HashSet<>(20);
// 从现有集合创建
Set<Double> prices = new HashSet<>(existingCollection);
2
基本操作
// 添加元素
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");
// 尝试添加重复元素
boolean added = hashSet.add("Apple"); // 返回false
// 删除元素
hashSet.remove("Banana");
// 检查元素是否存在
boolean hasApple = hashSet.contains("Apple"); // true
// 获取大小
int size = hashSet.size(); // 2
性能优化
HashSet的默认初始容量为16,负载因子为0.75。当元素数量达到容量*负载因子时,集合会自动扩容。在已知元素数量时设置合适的初始容量可以避免不必要的扩容操作,提升性能。
TreeSet详解
基于红黑树的有序Set实现,元素自动排序
核心特性
- 基于TreeMap实现,使用红黑树存储数据
- 元素按自然顺序或自定义Comparator排序
- 不允许null元素
- 添加、删除和查找操作的时间复杂度为O(log n)
1
创建TreeSet
import java.util.TreeSet;
import java.util.Set;
import java.util.Comparator;
// 自然顺序排序
Set<String> treeSet = new TreeSet<>();
// 自定义排序(按字符串长度)
Set<String> lengthSortedSet = new TreeSet<>(
Comparator.comparingInt(String::length)
);
// 从现有集合创建(元素必须实现Comparable)
Set<Integer> numbers = new TreeSet<>(existingCollection);
2
排序操作
// 添加元素
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Orange");
// 元素自动排序
System.out.println(treeSet); // [Apple, Banana, Orange]
// 获取第一个和最后一个元素
String first = treeSet.first(); // "Apple"
String last = treeSet.last(); // "Orange"
// 范围查找(子集)
Set<String> subSet = treeSet.subSet("Apple", "Orange");
重要限制
存储在TreeSet中的元素必须实现Comparable接口,或者在创建TreeSet时提供Comparator。否则在添加元素时会抛出ClassCastException。
LinkedHashSet详解
维护插入顺序的HashSet实现
核心特性
- 继承自HashSet,使用链表维护插入顺序
- 元素按插入顺序排序
- 允许null元素
- 性能略低于HashSet,但提供了可预测的迭代顺序
1
创建与基本操作
import java.util.LinkedHashSet;
import java.util.Set;
// 创建LinkedHashSet
Set<String> linkedHashSet = new LinkedHashSet<>();
// 添加元素
linkedHashSet.add("Apple");
linkedHashSet.add("Banana");
linkedHashSet.add("Orange");
// 迭代顺序与插入顺序一致
for (String fruit : linkedHashSet) {
System.out.println(fruit); // Apple, Banana, Orange
}
// 删除第一个元素
linkedHashSet.remove("Apple");
// 添加新元素到末尾
linkedHashSet.add("Mango");
2
访问顺序特性
// 插入顺序维护
Set<Integer> numbers = new LinkedHashSet<>();
numbers.add(3);
numbers.add(1);
numbers.add(4);
numbers.add(2);
System.out.println(numbers); // [3, 1, 4, 2]
// 即使元素被访问,顺序也不会改变
numbers.contains(1); // 访问元素
System.out.println(numbers); // 仍为 [3, 1, 4, 2]
使用场景
LinkedHashSet非常适合需要维护插入顺序,同时又要快速查找元素的场景。例如实现LRU缓存时,可以结合LinkedHashMap使用。
HashSet、TreeSet与LinkedHashSet对比
全面比较三种Set实现的特性与适用场景
HashSet
无序集合
性能最佳
允许null
TreeSet
排序集合
自动排序
禁止null
LinkedHashSet
插入顺序
快速迭代
允许null
特性 | HashSet | TreeSet | LinkedHashSet |
---|---|---|---|
底层实现 | 哈希表 | 红黑树 | 哈希表 + 双向链表 |
排序保证 | 无 | 自然顺序或自定义顺序 | 插入顺序 |
允许null元素 | 是 | 否 | 是 |
add/contains性能 | O(1) | O(log n) | O(1) |
迭代性能 | 慢(无顺序) | 中(有序) | 快(链表顺序) |
内存占用 | 低 | 中 | 高(维护链表) |
最佳使用场景 | 需要快速查找,不关心顺序 | 需要元素排序 | 需要维护插入顺序 |
常见错误与最佳实践
避免Set使用中的陷阱,掌握高效使用技巧
常见错误
- 未正确实现equals/hashCode导致重复元素
- 在TreeSet中使用未实现Comparable的对象
- 在迭代过程中修改集合导致ConcurrentModificationException
- 误认为HashSet是有序集合
- 对大集合使用contains()性能差
最佳实践
- 存储自定义对象时始终重写equals/hashCode
- 在已知元素数量时设置HashSet的初始容量
- 使用TreeSet时提供Comparator而不是依赖自然顺序
- 只读遍历使用Iterator而不是forEach+remove
- 对集合使用containsAll()代替嵌套循环
动手练习
- 实现一个自定义类,存储在HashSet中并正确重写equals/hashCode
- 创建TreeSet,按学生成绩降序排列
- 使用LinkedHashSet实现一个最近访问项列表
- 比较不同Set实现的10万次添加操作的性能
- 实现一个Set去重工具类