Set 接口 是 Java 集合框架中用于存储无序且不重复元素的集合。它继承自 Collection 接口,但与 List 不同,Set 不允许存储重复的元素。Set 的主要用途包括:
- 去重:自动确保集合中的元素唯一,适用于需要存储不重复元素的数据结构
- 高效查找:基于哈希(HashSet)、树(TreeSet)等机制实现,提供高效的查找和存储操作
| 方法签名 | 描述 |
|---|---|
| boolean add(E e) | 将元素添加到集合中。如果元素已存在,则不添加,返回 false |
| boolean remove(Object o) | 移除集合中指定元素。如果元素存在,则移除并返回 true |
| boolean contains(Object o) | 判断集合中是否存在指定的元素 |
| int size() | 返回集合中元素的数量 |
| void clear() | 移除集合中的所有元素 |
Java 提供了多种 Set 的实现,主要有以下几种:
- HashSet:基于哈希表实现的 Set,具有快速的插入、查找和删除操作,但不保证元素的顺序
- LinkedHashSet:基于哈希表和双向链表实现的 Set,保证元素的顺序与插入顺序相同
- TreeSet:基于红黑树实现的 Set,保证元素按照自然顺序或指定比较器的顺序进行排序
HashSet
HashSet 是最常用的 Set 实现类,基于哈希表实现。它不保证集合迭代的顺序,特别是不保证恒定顺序
import java.util.HashSet;
import java.util.Set;
public class HashSetExample {
public static void main(String[] args) {
Set<String> uniqueFruits = new HashSet<>();
uniqueFruits.add("Apple");
uniqueFruits.add("Banana");
uniqueFruits.add("Cherry");
uniqueFruits.add("Apple"); // 重复元素,不会被添加
System.out.println("HashSet: " + uniqueFruits); // 输出: [Apple, Banana, Cherry]
// 检查是否包含元素
boolean hasBanana = uniqueFruits.contains("Banana");
System.out.println("是否包含 Banana: " + hasBanana); // 输出: true
// 移除元素
uniqueFruits.remove("Cherry");
System.out.println("移除 Cherry 后: " + uniqueFruits); // 输出: [Apple, Banana]
}
}
linkedHashSet
LinkedHashSet 是 HashSet 的子类,它通过链表维护元素的插入顺序。与 HashSet 相比,LinkedHashSet 在迭代时能保持元素的插入顺序
import java.util.LinkedHashSet;
import java.util.Set;
public class LinkedHashSetExample {
public static void main(String[] args) {
Set<String> orderedFruits = new LinkedHashSet<>();
orderedFruits.add("Apple");
orderedFruits.add("Banana");
orderedFruits.add("Cherry");
orderedFruits.add("Apple"); // 重复元素,不会被添加
System.out.println("LinkedHashSet: " + orderedFruits); // 输出: [Apple, Banana, Cherry]
// 迭代顺序与添加顺序一致
for (String fruit : orderedFruits) {
System.out.println(fruit);
}
}
}
TreeSet
TreeSet 是 NavigableSet 接口的实现类,基于红黑树实现,能够确保元素按照其自然顺序或指定的比较器顺序进行排序。TreeSet 不允许存储 null 元素
import java.util.Set;
import java.util.TreeSet;
public class TreeSetExample {
public static void main(String[] args) {
Set<Integer> sortedNumbers = new TreeSet<>();
sortedNumbers.add(30);
sortedNumbers.add(10);
sortedNumbers.add(20);
sortedNumbers.add(40);
sortedNumbers.add(50);
System.out.println("TreeSet: " + sortedNumbers); // 输出: [10, 20, 30, 40, 50]
// 迭代顺序为排序后的顺序
for (Integer number : sortedNumbers) {
System.out.println(number);
}
// 使用自定义比较器(降序)
Set<Integer> descendingSet = new TreeSet<>((a, b) -> b - a);
descendingSet.addAll(sortedNumbers);
System.out.println("降序 TreeSet: " + descendingSet); // 输出: [50, 40, 30, 20, 10]
}
}
CopyOnWriteArraySet
CopyOnWriteArraySet 是 Set 接口的线程安全实现类,基于 CopyOnWriteArrayList 实现。它适用于读操作远多于写操作的场景,因为每次写操作都会复制整个底层数组。
import java.util.Set;
import java.util.concurrent.CopyOnWriteArraySet;
public class CopyOnWriteArraySetExample {
public static void main(String[] args) {
Set<String> threadSafeSet = new CopyOnWriteArraySet<>();
threadSafeSet.add("Apple");
threadSafeSet.add("Banana");
threadSafeSet.add("Cherry");
threadSafeSet.add("Banana"); // 重复元素,不会被添加
System.out.println("CopyOnWriteArraySet: " + threadSafeSet); // 输出: [Apple, Banana, Cherry]
// 线程安全的迭代
for (String fruit : threadSafeSet) {
System.out.println(fruit);
}
}
}
ConcurrentSkipListSet
ConcurrentSkipListSet 是一个基于跳表(Skip List)数据结构实现的线程安全的并发有序集合。它允许在 O(logn) 的平均时间复杂度内完成插入、删除和查找操作
Set<Integer> skipListSet = new ConcurrentSkipListSet<>();
skipListSet.add(5);
skipListSet.add(2);
System.out.println(skipListSet); // [2, 5](自动排序)
Set 选择
| 场景 | 推荐实现类 | 理由 |
|---|---|---|
| 快速去重、无需顺序 | HashSet | 性能最优,时间复杂度 O(1) |
| 去重且保留插入顺序 | LinkedHashSet | 维护插入顺序,性能接近 HashSet |
| 元素需排序或范围查询 | TreeSet | 基于红黑树,支持有序操作 |
| 高并发读、极少写 | CopyOnWriteArraySet | 线程安全,读操作无锁 |
| 高并发有序集合 | ConcurrentSkipListSet | 线程安全,支持排序和并发访问 |