300. Java Stream API - 收集 Stream 元素到集合或数组(Java Stream 收集器详解)

300. Java Stream API - 收集 Stream 元素到集合或数组(Java Stream 收集器详解)

Java 的 Stream API 提供了多种方式来将流中的元素收集到集合中,比如 List、Set、数组等。你在上一节已经见识过其中的一两个例子,本节我们来深入挖掘各种实用模式及其适用场景。


🧭 在选择收集方式前,你应先思考几个关键问题:

  • 🔒 是否需要一个 不可变(unmodifiable) 的集合?
  • 🧺 是否需要一个具体的集合类型(如 ArrayListLinkedList 或自定义实现)?
  • 📏 是否能预估元素数量?(这将影响性能优化)
  • 🧩 是否需要与第三方库的集合实现兼容?

这些因素会影响你最终的收集方式选择。接下来我们逐一介绍各种场景下的典型收集方式。


🚀 1. 收集到可变 ArrayList(默认方式)

这是最常用也最简单的方式:

Stream<String> strings = Stream.of("one", "two", "three", "four");

List<String> result = strings
    .filter(s -> s.length() == 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());

System.out.println("result = " + result);

🧾 输出:

result = [ONE, TWO]

📌 特点:

  • 返回一个普通的 ArrayList
  • 简洁 ✅,可变 ✅,性能中等 ⚠️(当元素数量大时,可能触发内部数组扩容)

🎯 2. 自定义集合类型或预设容量(toCollection

当你:

  • 想使用其他集合类型(如 LinkedList
  • 或者你知道将要收集多少元素(可优化容量)

可使用 Collectors.toCollection(...)

Stream<Integer> ints = IntStream.range(0, 10_000).boxed();

List<String> result = ints
    .map(String::valueOf)
    .collect(Collectors.toCollection(() -> new ArrayList<>(10_000)));

System.out.println("# result size = " + result.size());

🧠 好处:

  • 避免 ArrayList 多次扩容
  • 可替换为 LinkedList::newCopyOnWriteArrayList::new 等工厂方法

🔒 3. 收集为不可变 List(Java 10+)

✅ 使用 Collectors.toUnmodifiableList()

Stream<String> strings = Stream.of("one", "two", "three", "four");

List<String> result = strings
    .filter(s -> s.length() == 3)
    .map(String::toUpperCase)
    .collect(Collectors.toUnmodifiableList());

System.out.println("result = " + result);

📌 输出:

result = [ONE, TWO]

⚠️ 尝试修改 result.add(...) 会抛出 UnsupportedOperationException


🚀 4. 更高效的不可变 List(Java 16+)

✅ 使用新方法 .toList()

Stream<String> strings = Stream.of("one", "two", "three", "four");

List<String> result = strings
    .filter(s -> s.length() == 3)
    .map(String::toUpperCase)
    .toList();

System.out.println("result = " + result);

📌 输出:

result = [ONE, TWO]

⚙️ 为什么更高效?

  • .collect(Collectors.toUnmodifiableList()) 的底层实现是:
    1. 收集到一个 ArrayList
    2. 再封装为不可变集合(多了一步)
  • .toList() 则更聪明:如果能预估流的长度,它会一次性分配数组空间,避免扩容与拷贝JDK 16 优化)

🔢 5. 收集到数组(使用 toArray

❌ 简单版(类型擦除):

Object[] result = Stream.of("a", "b", "c").toArray();
  • 不推荐!返回 Object[],类型丢失。

✅ 推荐写法(保留数组类型):

String[] result = Stream.of("one", "two", "three", "four")
    .filter(s -> s.length() == 3)
    .map(String::toUpperCase)
    .toArray(String[]::new);

System.out.println(Arrays.toString(result));

📌 输出:

[ONE, TWO]

🧠 背后的语法糖:

String[]::new == size -> new String[size]

📝 各种收集方式总结对比表

模式 返回类型 可变性 Java 版本 适用场景说明
collect(Collectors.toList()) ArrayList ✅ 可变 Java 8+ 默认收集方式,简单好用
collect(toCollection(...)) 任意集合类型 ✅ 可变 Java 8+ 自定义集合或设置初始容量
collect(toUnmodifiableList()) ImmutableList ❌ 不可变 Java 10+ 需要只读集合,防止误修改
stream.toList() ImmutableList ❌ 不可变 Java 16+ 更高效的不可变收集,推荐
toArray(String[]::new) 数组 ✅ 可变 Java 8+ 需要收集成固定类型数组
toArray() Object[] ✅ 可变 Java 8+ 不推荐,类型擦除
©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容