301. Java Stream API - 元素收集方式详解(进阶版)

0 阅读2分钟

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()更高性能不可变 ListJava 16+
Collectors.toCollection(...)需要指定集合类型高度灵活
Collectors.toSet()结果需去重❌(返回 HashSet自动去重
Collectors.toUnmodifiableSet()去重且不可变Java 9+
toArray()返回数组✅(重载版)常用于需要原始数组场景