301. Java Stream API - 元素收集方式详解(进阶版)
在使用 Stream API 处理数据时,最终我们往往需要将处理结果“收集”到某种容器中。这个容器可以是 List、Set、自定义集合,甚至是一个数组。
🎯 为什么要关注收集方式?
选择不同的收集方式不仅会影响性能(如是否复制数组、是否预设容量等),还会影响返回结果的类型(是否可修改、是否线程安全等)。本节我们详细讲解各种收集方式的适用场景与实现方式。
🧰 收集到自定义集合(Homemade Collection)
当你需要将结果收集到特定类型的集合中,比如你自定义的列表类或是三方库的集合类型(比如 Apache Commons、Guava 的集合),可以使用:
.collect(Collectors.toCollection(Supplier))
这允许你灵活地定义要收集到哪种集合中。比如:
Stream<String> strings = Stream.of("one", "two", "three", "four");
List<String> result = strings
.filter(s -> s.length() == 3)
.map(String::toUpperCase)
.collect(Collectors.toCollection(LinkedList::new)); // 🔧 使用 LinkedList
System.out.println("Class of result = " + result.getClass());
System.out.println("result = " + result);
📌 输出:
Class of result = class java.util.LinkedList
result = [ONE, TWO]
✅ 说明:可以用这个方式指定任何 Collection 子类,包括第三方集合或者自定义集合。
🧩 收集到 Set(去重)
如果你希望收集的元素去重,可以使用 Collectors.toSet():
Stream<String> strings = Stream.of("one", "two", "three", "four");
Set<String> result = strings
.filter(s -> s.length() == 3)
.map(String::toUpperCase)
.collect(Collectors.toSet());
System.out.println("Class of result = " + result.getClass());
System.out.println("result = " + result);
📌 输出:
Class of result = class java.util.HashSet
result = [ONE, TWO]
✅ 说明:
- 默认返回的是
HashSet - 元素自动去重
- 元素顺序不可预测(因为是 HashSet)
🔒 收集到不可变 Set
如果你希望结果是不可变集合(防止被修改),可以使用:
.collect(Collectors.toUnmodifiableSet());
示例:
Stream<String> strings = Stream.of("one", "two", "three", "four");
Set<String> result = strings
.filter(s -> s.length() == 3)
.map(String::toUpperCase)
.collect(Collectors.toUnmodifiableSet());
System.out.println("Class of result = " + result.getClass());
System.out.println("result = " + result);
📌 输出:
Class of result = class java.util.ImmutableCollections$Set12
result = [ONE, TWO]
✅ 说明:尝试修改这个集合会抛出 UnsupportedOperationException 异常,适合用于不可变数据流转。
📦 收集到数组(Array)
🌱 toArray()(基础版本)
最基础的 toArray() 方法会返回 Object[]:
Object[] arr = stream.toArray();
缺点:丢失具体类型信息,需要强制类型转换。
🌳 toArray(IntFunction<A[]> generator)
推荐使用的版本,可以指定返回类型:
Stream<String> strings = Stream.of("one", "two", "three", "four");
String[] result = strings
.filter(s -> s.length() == 3)
.map(String::toUpperCase)
.toArray(String[]::new); // 👈 使用构造方法引用
System.out.println("result = " + Arrays.toString(result));
📌 输出:
result = [ONE, TWO]
✅ 说明:
String[]::new是构造数组的函数- 更安全,更推荐
💡 总结:不同收集方式对比
| 收集方式 | 适用场景 | 是否可变 | 是否可指定容器类型 | 特点 |
|---|---|---|---|---|
Collectors.toList() | 普通场景 | ✅ | ❌(返回 ArrayList) | 最常用 |
Collectors.toUnmodifiableList() | 安全性优先 | ❌ | ❌ | Java 9+ |
Stream.toList() | 更高性能不可变 List | ❌ | ❌ | Java 16+ |
Collectors.toCollection(...) | 需要指定集合类型 | ✅ | ✅ | 高度灵活 |
Collectors.toSet() | 结果需去重 | ✅ | ❌(返回 HashSet) | 自动去重 |
Collectors.toUnmodifiableSet() | 去重且不可变 | ❌ | ❌ | Java 9+ |
toArray() | 返回数组 | ✅ | ✅(重载版) | 常用于需要原始数组场景 |