一、Set接口概述
1.1 Set接口特点
import java.util.*;
public class SetOverview {
public static void main(String[] args) {
// Set接口的特点:
// 1. 不允许重复元素(基于equals()和hashCode()判断)
// 2. 最多允许一个null元素
// 3. 不保证元素的顺序(某些实现如TreeSet保证排序)
// 4. 没有get(index)方法(因为无序)
Set<String> set = new HashSet<>();
// 添加元素
System.out.println("添加Apple: " + set.add("Apple")); // true
System.out.println("添加Banana: " + set.add("Banana")); // true
System.out.println("重复添加Apple: " + set.add("Apple")); // false
set.add("Orange");
set.add("Grape");
set.add(null); // 允许一个null
System.out.println("Set集合: " + set);
System.out.println("大小: " + set.size());
System.out.println("是否包含Banana: " + set.contains("Banana"));
System.out.println("是否为空: " + set.isEmpty());
}
}
二、Set的主要实现类
2.1 HashSet(哈希集合)
import java.util.*;
public class HashSetDemo {
public static void main(String[] args) {
// HashSet:基于HashMap实现
// 特点:无序、允许null、查找效率高(O(1))
Set<String> hashSet = new HashSet<>();
// 1. 基本操作
hashSet.add("Java");
hashSet.add("Python");
hashSet.add("C++");
hashSet.add("JavaScript");
hashSet.add("Java"); // 重复,不会被添加
System.out.println("HashSet: " + hashSet);
// 2. 批量操作
Set<String> anotherSet = new HashSet<>();
anotherSet.add("Go");
anotherSet.add("Rust");
anotherSet.add("Java");
hashSet.addAll(anotherSet); // 并集
System.out.println("添加后: " + hashSet);
// 3. 删除操作
hashSet.remove("C++");
hashSet.removeIf(lang -> lang.startsWith("J")); // Java 8+
System.out.println("删除后: " + hashSet);
// 4. 遍历方式
// 增强for循环
for (String lang : hashSet) {
System.out.print(lang + " ");
}
System.out.println();
// 迭代器
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.print(iterator.next() + " ");
}
System.out.println();
// forEach方法
hashSet.forEach(lang -> System.out.print(lang + " "));
System.out.println();
// 5. 构造方法示例
Set<Integer> numbers = new HashSet<>(20); // 指定初始容量
numbers.addAll(Arrays.asList(1, 2, 3, 4, 5));
// 从已有集合创建
Set<String> copySet = new HashSet<>(hashSet);
// 6. 性能测试
Set<Integer> largeSet = new HashSet<>();
long start = System.nanoTime();
for (int i = 0; i < 100000; i++) {
largeSet.add(i);
}
long addTime = System.nanoTime() - start;
System.out.println("\n添加100000个元素耗时: " + (addTime / 1000000) + "ms");
start = System.nanoTime();
boolean contains = largeSet.contains(50000);
long searchTime = System.nanoTime() - start;
System.out.println("查找元素耗时: " + searchTime + "ns");
}
}
2.2 LinkedHashSet(有序哈希集合)
import java.util.*;
public class LinkedHashSetDemo {
public static void main(String[] args) {
// LinkedHashSet:继承自HashSet,基于LinkedHashMap实现
// 特点:维护插入顺序,迭代顺序与插入顺序一致
Set<String> linkedHashSet = new LinkedHashSet<>();
// 按顺序添加元素
linkedHashSet.add("First");
linkedHashSet.add("Second");
linkedHashSet.add("Third");
linkedHashSet.add("Fourth");
linkedHashSet.add("Fifth");
linkedHashSet.add("Second"); // 重复,不会添加
System.out.println("LinkedHashSet(保持插入顺序): " + linkedHashSet);
// 遍历会按照插入顺序
System.out.print("遍历顺序: ");
for (String s : linkedHashSet) {
System.out.print(s + " ");
}
System.out.println();
// 访问顺序示例(LinkedHashMap的特性)
// LinkedHashSet本身不支持访问顺序,但LinkedHashMap支持
// 如果需要访问顺序,需要使用LinkedHashMap
// LinkedHashSet构造方法
Set<Integer> numbers = new LinkedHashSet<>(Arrays.asList(3, 1, 4, 1, 5, 9));
System.out.println("数字(保持插入顺序): " + numbers);
// 性能对比
testPerformance();
}
private static void testPerformance() {
int size = 100000;
// HashSet
Set<Integer> hashSet = new HashSet<>();
long start = System.nanoTime();
for (int i = 0; i < size; i++) {
hashSet.add(i);
}
long hashSetTime = System.nanoTime() - start;
// LinkedHashSet
Set<Integer> linkedHashSet = new LinkedHashSet<>();
start = System.nanoTime();
for (int i = 0; i < size; i++) {
linkedHashSet.add(i);
}
long linkedHashSetTime = System.nanoTime() - start;
System.out.println("\n性能对比(添加" + size + "个元素):");
System.out.println("HashSet: " + (hashSetTime / 1000000) + "ms");
System.out.println("LinkedHashSet: " + (linkedHashSetTime / 1000000) + "ms");
System.out.println("LinkedHashSet稍慢,因为需要维护链表结构");
}
}
2.3 TreeSet(树集合)
import java.util.*;
public class TreeSetDemo {
public static void main(String[] args) {
// TreeSet:基于TreeMap(红黑树)实现
// 特点:元素自动排序,不允许null(因为需要比较)
// 1. 自然排序(元素必须实现Comparable接口)
TreeSet<String> treeSet = new TreeSet<>();
treeSet.add("Banana");
treeSet.add("Apple");
treeSet.add("Orange");
treeSet.add("Grape");
treeSet.add("Cherry");
System.out.println("TreeSet(自动排序): " + treeSet);
// 2. 自定义排序
TreeSet<Integer> numbers = new TreeSet<>(Collections.reverseOrder());
numbers.addAll(Arrays.asList(5, 2, 8, 1, 9, 3));
System.out.println("降序TreeSet: " + numbers);
// 3. TreeSet特有方法
System.out.println("\n--- TreeSet特有方法 ---");
// 第一个和最后一个
System.out.println("第一个元素: " + treeSet.first());
System.out.println("最后一个元素: " + treeSet.last());
// 小于/大于某个元素
System.out.println("小于'Grape'的最大元素: " + treeSet.lower("Grape"));
System.out.println("大于'Grape'的最小元素: " + treeSet.higher("Grape"));
// 子集
System.out.println("从Apple到Grape(包含): " +
treeSet.subSet("Apple", true, "Grape", true));
System.out.println("从Apple到Grape(不包含Grape): " +
treeSet.subSet("Apple", true, "Grape", false));
// 头集和尾集
System.out.println("小于'Orange'的元素: " + treeSet.headSet("Orange"));
System.out.println("大于等于'Orange'的元素: " + treeSet.tailSet("Orange"));
// 4. 使用Comparator
TreeSet<Person> people = new TreeSet<>(Comparator
.comparing(Person::getAge)
.thenComparing(Person::getName));
people.add(new Person("Alice", 25));
people.add(new Person("Bob", 30));
people.add(new Person("Charlie", 25)); // 年龄相同,按名字排序
people.add(new Person("David", 28));
System.out.println("\n按年龄排序: " + people);
// 5. 遍历
System.out.print("正向遍历: ");
for (String fruit : treeSet) {
System.out.print(fruit + " ");
}
System.out.println();
System.out.print("反向遍历: ");
Iterator<String> descendingIterator = treeSet.descendingIterator();
while (descendingIterator.hasNext()) {
System.out.print(descendingIterator.next() + " ");
}
System.out.println();
}
static class Person {
String name;
int age;
Person(String name, int age) {
this.name = name;
this.age = age;
}
String getName() { return name; }
int getAge() { return age; }
@Override
public String toString() {
return name + "(" + age + ")";
}
}
}
三、Set的数学运算
3.1 集合运算(并集、交集、差集)
import java.util.*;
public class SetOperations {
public static void main(String[] args) {
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
System.out.println("集合A: " + set1);
System.out.println("集合B: " + set2);
// 1. 并集(Union)
Set<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("并集 A∪B: " + union);
// 2. 交集(Intersection)
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("交集 A∩B: " + intersection);
// 3. 差集(Difference)
Set<Integer> difference = new HashSet<>(set1);
difference.removeAll(set2);
System.out.println("差集 A-B: " + difference);
// 4. 对称差集(Symmetric Difference)- 在A或B中,但不同时在A和B中
Set<Integer> symmetricDifference = new HashSet<>(union);
symmetricDifference.removeAll(intersection);
System.out.println("对称差集: " + symmetricDifference);
// 5. 子集判断
Set<Integer> subset = new HashSet<>(Arrays.asList(2, 3));
System.out.println("{2,3}是否是A的子集: " + set1.containsAll(subset));
// 6. 相等判断
Set<Integer> set3 = new HashSet<>(Arrays.asList(5, 4, 3, 2, 1));
System.out.println("A和{5,4,3,2,1}是否相等: " + set1.equals(set3));
// 7. 补集(Complement)示例
Set<Integer> universalSet = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
Set<Integer> complement = new HashSet<>(universalSet);
complement.removeAll(set1);
System.out.println("A在全集{1..10}中的补集: " + complement);
}
}
四、自定义对象在Set中的使用
4.1 重写equals()和hashCode()
import java.util.*;
public class CustomObjectInSet {
public static void main(String[] args) {
// 对于自定义对象,要正确使用Set,必须重写equals()和hashCode()
Set<Student> studentSet = new HashSet<>();
Student s1 = new Student("001", "Alice", 20);
Student s2 = new Student("002", "Bob", 21);
Student s3 = new Student("001", "Alice", 20); // 与s1相同
System.out.println("添加s1: " + studentSet.add(s1)); // true
System.out.println("添加s2: " + studentSet.add(s2)); // true
System.out.println("添加s3(与s1相同): " + studentSet.add(s3)); // false
System.out.println("学生集合: " + studentSet);
System.out.println("集合大小: " + studentSet.size());
// TreeSet中的自定义对象必须实现Comparable或提供Comparator
TreeSet<Student> treeSet = new TreeSet<>(Comparator
.comparing(Student::getAge)
.thenComparing(Student::getName));
treeSet.add(s1);
treeSet.add(s2);
treeSet.add(new Student("003", "Charlie", 19));
System.out.println("\nTreeSet(按年龄排序): " + treeSet);
}
static class Student {
private String id;
private String name;
private int age;
public Student(String id, String name, int age) {
this.id = id;
this.name = name;
this.age = age;
}
// 重写equals()方法
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return Objects.equals(id, student.id);
}
// 重写hashCode()方法
@Override
public int hashCode() {
return Objects.hash(id);
}
// 重写toString()方法
@Override
public String toString() {
return name + "(" + id + ", " + age + ")";
}
// Getter方法
public String getId() { return id; }
public String getName() { return name; }
public int getAge() { return age; }
}
}
五、线程安全的Set实现
5.1 同步包装和并发Set
import java.util.*;
import java.util.concurrent.*;
public class ThreadSafeSets {
public static void main(String[] args) throws InterruptedException {
System.out.println("=== 线程安全的Set实现 ===\n");
// 1. Collections.synchronizedSet(传统方式)
Set<String> synchronizedSet = Collections.synchronizedSet(new HashSet<>());
// 遍历时需要手动同步
synchronized(synchronizedSet) {
for (String s : synchronizedSet) {
System.out.println(s);
}
}
// 2. CopyOnWriteArraySet(适合读多写少的场景)
// 写操作时复制整个数组,读操作不需要锁
CopyOnWriteArraySet<String> copyOnWriteSet = new CopyOnWriteArraySet<>();
copyOnWriteSet.add("Item1");
copyOnWriteSet.add("Item2");
copyOnWriteSet.addIfAbsent("Item1"); // 如果不存在则添加
System.out.println("CopyOnWriteArraySet: " + copyOnWriteSet);
// 遍历时是安全的
for (String item : copyOnWriteSet) {
System.out.println(item);
copyOnWriteSet.add("NewItem"); // 不会影响当前遍历
}
// 3. ConcurrentHashMap.newKeySet()(Java 8+)
Set<String> concurrentSet = ConcurrentHashMap.newKeySet();
concurrentSet.add("Concurrent1");
concurrentSet.add("Concurrent2");
System.out.println("ConcurrentHashMap KeySet: " + concurrentSet);
// 4. 性能测试
testPerformance();
}
private static void testPerformance() throws InterruptedException {
int threads = 10;
int iterations = 10000;
System.out.println("\n=== 性能测试 ===");
// HashSet(非线程安全)
Set<Integer> hashSet = new HashSet<>();
testSetPerformance(hashSet, "HashSet", threads, iterations);
// synchronizedSet
Set<Integer> syncSet = Collections.synchronizedSet(new HashSet<>());
testSetPerformance(syncSet, "SynchronizedSet", threads, iterations);
// CopyOnWriteArraySet
Set<Integer> cowSet = new CopyOnWriteArraySet<>();
testSetPerformance(cowSet, "CopyOnWriteArraySet", threads, iterations);
// ConcurrentHashMap KeySet
Set<Integer> concurrentSet = ConcurrentHashMap.newKeySet();
testSetPerformance(concurrentSet, "ConcurrentKeySet", threads, iterations);
}
private static void testSetPerformance(Set<Integer> set, String name,
int threads, int iterations)
throws InterruptedException {
ExecutorService executor = Executors.newFixedThreadPool(threads);
long startTime = System.currentTimeMillis();
// 创建多个任务并发执行
List<Callable<Void>> tasks = new ArrayList<>();
for (int t = 0; t < threads; t++) {
final int threadId = t;
tasks.add(() -> {
for (int i = 0; i < iterations; i++) {
int value = threadId * iterations + i;
set.add(value);
set.contains(value);
if (set.size() > 1000) {
set.remove(value - 500);
}
}
return null;
});
}
// 执行所有任务
executor.invokeAll(tasks);
executor.shutdown();
executor.awaitTermination(1, TimeUnit.MINUTES);
long endTime = System.currentTimeMillis();
System.out.printf("%-25s: %5d ms, 大小: %d%n",
name, endTime - startTime, set.size());
}
}
六、Set的性能比较和选择策略
6.1 性能比较
import java.util.*;
public class SetPerformanceComparison {
public static void main(String[] args) {
int size = 100000;
System.out.println("=== Set实现类性能比较 ===\n");
// HashSet
Set<Integer> hashSet = new HashSet<>();
long hashSetTime = testAddAndContains(hashSet, size);
// LinkedHashSet
Set<Integer> linkedHashSet = new LinkedHashSet<>();
long linkedHashSetTime = testAddAndContains(linkedHashSet, size);
// TreeSet
Set<Integer> treeSet = new TreeSet<>();
long treeSetTime = testAddAndContains(treeSet, size);
System.out.println("添加和查找 " + size + " 个元素的耗时:");
System.out.println("HashSet: " + hashSetTime + "ms");
System.out.println("LinkedHashSet: " + linkedHashSetTime + "ms");
System.out.println("TreeSet: " + treeSetTime + "ms");
System.out.println("\n=== 内存占用比较 ===");
printMemoryUsage();
}
private static long testAddAndContains(Set<Integer> set, int size) {
long start = System.currentTimeMillis();
// 添加元素
for (int i = 0; i < size; i++) {
set.add(i);
}
// 查找元素
for (int i = 0; i < size; i++) {
set.contains(i);
}
return System.currentTimeMillis() - start;
}
private static void printMemoryUsage() {
Runtime runtime = Runtime.getRuntime();
Set<Object> hashSet = new HashSet<>();
Set<Object> linkedHashSet = new LinkedHashSet<>();
Set<Object> treeSet = new TreeSet<>();
// 添加一些元素
for (int i = 0; i < 10000; i++) {
hashSet.add("String_" + i);
linkedHashSet.add("String_" + i);
treeSet.add("String_" + i);
}
System.gc();
long hashSetMemory = runtime.totalMemory() - runtime.freeMemory();
System.gc();
long linkedHashSetMemory = runtime.totalMemory() - runtime.freeMemory();
System.gc();
long treeSetMemory = runtime.totalMemory() - runtime.freeMemory();
System.out.println("HashSet内存占用: ~" + (hashSetMemory/1024) + "KB");
System.out.println("LinkedHashSet内存占用: ~" + (linkedHashSetMemory/1024) + "KB");
System.out.println("TreeSet内存占用: ~" + (treeSetMemory/1024) + "KB");
}
}
6.2 选择策略
public class SetSelectionGuide {
/*
Set实现类选择指南:
- HashSet(最常用)
-
场景:需要快速查找,不关心顺序
-
优点:添加、删除、查找最快(O(1))
-
缺点:无序
- LinkedHashSet
-
场景:需要保持插入顺序
-
优点:保持插入顺序,查找速度快
-
缺点:比HashSet稍慢,内存占用稍大
- TreeSet
-
场景:需要排序或范围查找
-
优点:自动排序,支持范围操作
-
缺点:操作较慢(O(log n)),不允许null
- CopyOnWriteArraySet
-
场景:读多写少的并发环境
-
优点:读操作不需要锁
-
缺点:写操作性能差,内存占用大
- ConcurrentHashMap.newKeySet()
-
场景:高并发环境
-
优点:并发性能好
-
缺点:没有范围操作
*/
public static void main(String[] args) {
// 示例:根据不同场景选择Set
// 场景1:存储不重复的用户ID,不关心顺序
System.out.println("场景1:用户ID集合(快速查找)");
Set<String> userIds = new HashSet<>();
// 场景2:记录用户访问页面的顺序
System.out.println("场景2:用户访问历史(保持顺序)");
Set<String> visitHistory = new LinkedHashSet<>();
// 场景3:按分数排序的学生集合
System.out.println("场景3:学生成绩排名(需要排序)");
Set<StudentScore> studentScores = new TreeSet<>(Comparator
.comparing(StudentScore::getScore).reversed());
// 场景4:多线程共享的配置集合
System.out.println("场景4:多线程配置集合");
Set<String> configSet = ConcurrentHashMap.newKeySet();
}
static class StudentScore {
String name;
int score;
// ... 省略构造方法和getter
}
}
七、Java 8+ 新特性在Set中的应用
7.1 Stream API操作Set
import java.util.*;
import java.util.stream.*;
public class SetStreamOperations {
public static void main(String[] args) {
Set<Integer> numbers = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));
System.out.println("原始集合: " + numbers);
// 1. 过滤
Set<Integer> evenNumbers = numbers.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toSet());
System.out.println("偶数集合: " + evenNumbers);
// 2. 映射
Set<String> squaredStrings = numbers.stream()
.map(n -> n + "^2=" + (n * n))
.collect(Collectors.toCollection(LinkedHashSet::new)); // 保持顺序
System.out.println("平方映射: " + squaredStrings);
// 3. 排序
List<Integer> sortedList = numbers.stream()
.sorted()
.collect(Collectors.toList());
System.out.println("排序后: " + sortedList);
// 4. 统计
long count = numbers.stream().count();
int sum = numbers.stream().mapToInt(Integer::intValue).sum();
Optional<Integer> max = numbers.stream().max(Integer::compare);
Optional<Integer> min = numbers.stream().min(Integer::compare);
System.out.printf("统计: 数量=%d, 和=%d, 最大=%s, 最小=%s%n",
count, sum, max.orElse(null), min.orElse(null));
// 5. 查找
Optional<Integer> firstEven = numbers.stream()
.filter(n -> n % 2 == 0)
.findFirst();
System.out.println("第一个偶数: " + firstEven.orElse(null));
// 6. 匹配
boolean allEven = numbers.stream().allMatch(n -> n % 2 == 0);
boolean anyEven = numbers.stream().anyMatch(n -> n % 2 == 0);
boolean noneNegative = numbers.stream().noneMatch(n -> n < 0);
System.out.printf("匹配: 全偶数=%s, 有偶数=%s, 无负数=%s%n",
allEven, anyEven, noneNegative);
// 7. 去重(Set本身就去重,这里演示其他用途)
List<Integer> withDuplicates = Arrays.asList(1, 2, 2, 3, 3, 3, 4);
Set<Integer> distinct = withDuplicates.stream()
.collect(Collectors.toSet());
System.out.println("去重: " + distinct);
// 8. 并行流
Set<Integer> parallelProcessed = numbers.parallelStream()
.map(n -> n * 2)
.collect(Collectors.toSet());
System.out.println("并行处理: " + parallelProcessed);
// 9. 分组
Map<String, Set<Integer>> grouped = numbers.stream()
.collect(Collectors.groupingBy(
n -> n % 2 == 0 ? "偶数" : "奇数",
Collectors.toSet()
));
System.out.println("分组: " + grouped);
// 10. 创建不可变Set(Java 9+)
Set<String> immutableSet = Set.of("A", "B", "C");
// immutableSet.add("D"); // 抛出UnsupportedOperationException
System.out.println("不可变Set: " + immutableSet);
// 11. 复制Set
Set<String> original = new HashSet<>(Arrays.asList("X", "Y", "Z"));
Set<String> copy = Set.copyOf(original); // Java 10+
System.out.println("复制Set: " + copy);
}
}
八、Set实用工具类
8.1 Set工具类示例
import java.util.*;
public class SetUtils {
/**
* 计算两个Set的并集
*/
public static <T> Set<T> union(Set<T> set1, Set<T> set2) {
Set<T> result = new HashSet<>(set1);
result.addAll(set2);
return result;
}
/**
* 计算两个Set的交集
*/
public static <T> Set<T> intersection(Set<T> set1, Set<T> set2) {
Set<T> result = new HashSet<>(set1);
result.retainAll(set2);
return result;
}
/**
* 计算两个Set的差集(set1 - set2)
*/
public static <T> Set<T> difference(Set<T> set1, Set<T> set2) {
Set<T> result = new HashSet<>(set1);
result.removeAll(set2);
return result;
}
/**
* 计算两个Set的对称差集
*/
public static <T> Set<T> symmetricDifference(Set<T> set1, Set<T> set2) {
Set<T> union = union(set1, set2);
Set<T> intersection = intersection(set1, set2);
return difference(union, intersection);
}
/**
* 判断set1是否是set2的子集
*/
public static <T> boolean isSubset(Set<T> set1, Set<T> set2) {
return set2.containsAll(set1);
}
/**
* 判断set1是否是set2的真子集
*/
public static <T> boolean isProperSubset(Set<T> set1, Set<T> set2) {
return isSubset(set1, set2) && set1.size() < set2.size();
}
/**
* 将Set转换为排序的List
*/
public static <T extends Comparable<T>> List<T> toSortedList(Set<T> set) {
List<T> list = new ArrayList<>(set);
Collections.sort(list);
return list;
}
/**
* 获取Set中的最大元素
*/
public static <T extends Comparable<T>> Optional<T> max(Set<T> set) {
return set.stream().max(Comparable::compareTo);
}
/**
* 获取Set中的最小元素
*/
public static <T extends Comparable<T>> Optional<T> min(Set<T> set) {
return set.stream().min(Comparable::compareTo);
}
/**
* 随机获取Set中的一个元素
*/
public static <T> Optional<T> randomElement(Set<T> set) {
if (set.isEmpty()) {
return Optional.empty();
}
int randomIndex = new Random().nextInt(set.size());
Iterator<T> iterator = set.iterator();
for (int i = 0; i < randomIndex; i++) {
iterator.next();
}
return Optional.of(iterator.next());
}
/**
* 测试工具类
*/
public static void main(String[] args) {
Set<Integer> A = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> B = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));
System.out.println("集合A: " + A);
System.out.println("集合B: " + B);
System.out.println("并集: " + union(A, B));
System.out.println("交集: " + intersection(A, B));
System.out.println("差集(A-B): " + difference(A, B));
System.out.println("对称差集: " + symmetricDifference(A, B));
System.out.println("A是B的子集: " + isSubset(A, B));
System.out.println("{1,2}是A的真子集: " + isProperSubset(new HashSet<>(Arrays.asList(1, 2)), A));
Set<Integer> unsorted = new HashSet<>(Arrays.asList(5, 2, 8, 1, 3));
System.out.println("排序后的列表: " + toSortedList(unsorted));
System.out.println("最大值: " + max(unsorted).orElse(null));
System.out.println("最小值: " + min(unsorted).orElse(null));
System.out.println("随机元素: " + randomElement(unsorted).orElse(null));
}
}
总结
核心知识点:
-
Set接口特性:不允许重复、无序(某些实现有序)、最多一个null
-
HashSet vs LinkedHashSet vs TreeSet:根据是否需要排序或保持插入顺序选择
-
equals()和hashCode():自定义对象必须正确重写才能在Set中正常工作
-
线程安全:使用并发集合或同步包装
-
集合运算:并集、交集、差集、对称差集
最佳实践:
· 大多数情况使用HashSet
· 需要保持插入顺序时使用LinkedHashSet
· 需要排序或范围操作时使用TreeSet
· 多线程环境使用并发集合
· 自定义对象必须正确实现equals()和hashCode()
· 合理利用Stream API简化操作