279. Java Stream API - Stream 拼接的两种方式:concat() vs flatMap()

文章目录

  • [279. Java Stream API - Stream 拼接的两种方式:`concat()` vs `flatMap()`](#279. Java Stream API - Stream 拼接的两种方式:concat() vs flatMap())
      • [🎯 本节目标](#🎯 本节目标)
    • [🧪 背景问题:多个集合如何组合成一个流?](#🧪 背景问题:多个集合如何组合成一个流?)
    • [✅ 方式一:使用 `Stream.concat()`](#✅ 方式一:使用 Stream.concat())
      • [📌 特点:](#📌 特点:)
      • [🔨 示例代码:](#🔨 示例代码:)
      • [🔽 输出:](#🔽 输出:)
    • [❌ 局限:只能拼接两个流!](#❌ 局限:只能拼接两个流!)
    • [✅ 方式二:使用 `flatMap()` 动态拼接多个流](#✅ 方式二:使用 flatMap() 动态拼接多个流)
      • [📌 特点:](#📌 特点:)
      • [🔨 示例代码:](#🔨 示例代码:)
      • [🔽 输出:](#🔽 输出:)
    • [🧠 为什么 `concat()` 不用可变参数(`varargs`)?](#🧠 为什么 concat() 不用可变参数(varargs)?)
    • [🔍 性能对比与底层机制](#🔍 性能对比与底层机制)
      • [🚦 关于 SIZED 特性小贴士:](#🚦 关于 SIZED 特性小贴士:)
    • [🔚 总结选择建议](#🔚 总结选择建议)
    • [🔁 拓展练习题](#🔁 拓展练习题)
      • [☑️ 合并三个列表并打印奇数元素](#☑️ 合并三个列表并打印奇数元素)

279. Java Stream API - Stream 拼接的两种方式:concat() vs flatMap()


🎯 本节目标

  • 理解 Stream.concat()flatMap() 实现流拼接的方式
  • 掌握两种方法的使用场景、性能差异与行为特性
  • 明确什么时候使用哪种方式更合适

🧪 背景问题:多个集合如何组合成一个流?

假设我们有多个 List<Integer>,我们想要将它们的元素拼接成一个大集合。


✅ 方式一:使用 Stream.concat()

📌 特点:

  • 适用于 拼接两个
  • 顺序是:先处理第一个流,再处理第二个流
  • 返回的流具有 SIZED 属性(已知大小)

🔨 示例代码:

java 复制代码
List<Integer> list0 = List.of(1, 2, 3);
List<Integer> list1 = List.of(4, 5, 6);

List<Integer> concat = Stream.concat(list0.stream(), list1.stream())
                             .toList();

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

🔽 输出:

java 复制代码
concat = [1, 2, 3, 4, 5, 6]

🧠 类比:就像把两个数组首尾缝在一起,中间没有拆分、合并的额外动作。


❌ 局限:只能拼接两个流!

如果你有 3 个、4 个、N 个流,就得嵌套调用:

java 复制代码
Stream<Integer> result = Stream.concat(
                             Stream.concat(list0.stream(), list1.stream()),
                             list2.stream()
                         );

📉 问题:每 concat 一次,就会生成一个临时中间流,内存和效率都有开销!


✅ 方式二:使用 flatMap() 动态拼接多个流

📌 特点:

  • 支持 多个流 拼接(不限数量)
  • 性能更优:只创建一个外层流
  • 缺点是:返回的流没有 SIZED 属性(大小未知)

🔨 示例代码:

java 复制代码
List<Integer> list0 = List.of(1, 2, 3);
List<Integer> list1 = List.of(4, 5, 6);
List<Integer> list2 = List.of(7, 8, 9);

List<Integer> flatMap = Stream.of(list0.stream(), list1.stream(), list2.stream())
                              .flatMap(Function.identity()) // 展平流
                              .toList();

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

🔽 输出:

java 复制代码
flatMap = [1, 2, 3, 4, 5, 6, 7, 8, 9]

🧠 类比:像是一个"展开三明治"------先把所有流打包,再逐个展开并合并元素。


🧠 为什么 concat() 不用可变参数(varargs)?

你可能好奇:为什么 concat() 不能接收多个流,比如这样:

java 复制代码
Stream.concat(stream1, stream2, stream3); // ❌ 不支持

📌 原因是:

  • 每次 concat() 只能拼接两个流
  • 若要拼接多个,建议使用 flatMap() 来减少中间流的创建

🔍 性能对比与底层机制

特性 concat() flatMap()
支持多个流拼接 否(只能两个) ✅ 支持任意多个流
是否创建中间临时流 是(每两个拼接都创建) 否(只创建一个外层流)
返回流是否有大小信息 ✅ 是(SIZED) ❌ 否(不再是 SIZED)
内部优化可能性 更易优化 较难优化

🚦 关于 SIZED 特性小贴士:

  • 如果你使用 Stream.concat(),结果流的大小是可预知的 → Stream 会自动标记为 SIZED
  • 如果使用 flatMap(),因为展开过程中元素数不确定,所以结果流被标记为 UNKNOWN

🔧 有些操作(如并行流优化、预分配内存)依赖 SIZED 特性,因此 concat 在某些场景下有优势。


🔚 总结选择建议

场景 建议使用
只拼接两个流 concat()
拼接多个流 flatMap()
希望保留流大小信息(如并行优化) concat()
性能优先、避免中间流创建 flatMap()

🔁 拓展练习题

☑️ 合并三个列表并打印奇数元素

java 复制代码
List<Integer> combined = Stream.of(list0.stream(), list1.stream(), list2.stream())
                               .flatMap(Function.identity())
                               .filter(n -> n % 2 != 0)
                               .toList();

System.out.println("odd numbers = " + combined);
相关推荐
咩咩啃树皮7 小时前
第40篇:Vue3组件化开发精讲——组件拆分、复用、父子通信、工程化架构
java·前端·架构
灯澜忆梦7 小时前
GO_并发编程---定时器
开发语言·后端·golang
鱟鲥鳚8 小时前
Spring Boot 集成 LangChain4j:从模型调用到 Tool Calling(Demo版)
java·spring boot
-银雾鸢尾-8 小时前
C#中的StringBuilder相关方法
开发语言·c#
-银雾鸢尾-8 小时前
C#中结构体与类的区别;抽象类与接口的区别
开发语言·c#
大模型码小白9 小时前
【Python零基础教程】继承、多态与魔法函数:面向对象编程三大核心特性详解
java·大数据·开发语言·人工智能·python·ai编程
腾渊信息科技公司10 小时前
Spring Boot对接MES实战:视觉检测数据自动同步方案
java·人工智能·spring boot·后端·计算机视觉·ai·软件需求
爱笑的源码基地11 小时前
高并发 Redis 缓存门诊HIS系统源码,含财务统计药房进销存
java·程序·门诊系统·诊所系统·云诊所源码
wuqingshun31415911 小时前
TCP超时重传机制是为了解决什么问题?
java
段一凡-华北理工大学11 小时前
向量数据库实战:选型、调优与落地~系列文章12:文本分块策略实战:chunk_size 怎么选?重叠多少?
开发语言·数据库·后端·oracle·rust·工业智能体·高炉智能化