stream.concat()适用于两个同类型、来源稳定、已知大小的流顺序合并,如内存中list转流后统一处理;不支持多流嵌套拼接,应改用stream.of().flatmap();流不可重复消费,需从原始集合重新生成。

Stream.concat() 是 Java Stream API 中最直接的流拼接工具,但它不是万能胶——用对场景才真正高效。
什么时候该用 Stream.concat()
适合两个同类型、来源稳定、已知大小的流顺序合并。比如把两个本地 List 转成的流拼起来,再统一过滤或映射:
- 两个 List 都来自内存(如配置项、缓存数据),无 IO 或异常风险
- 你明确只需要拼两个流,不打算扩展到三个以上
- 后续操作依赖流的 SIZED 特性(例如某些并行优化或 size() 估算)
- 示例:合并用户白名单和黑名单 ID 流后去重 → Stream.concat(whitelist.stream(), blacklist.stream()).distinct()
常见错误与规避方法
流是一次性的,消费过就不能再用。下面写法会报 IllegalStateException:
Streams1.forEach(System.out::println); // s1 已关闭
Stream.concat(s1, Stream.of("c"));
正确做法:
- 从原始集合重新生成流:用 list1.stream() 和 list2.stream(),而不是复用已消费的流变量
- 若流来自复杂逻辑(如数据库查询),先 collect 到 List 再转流,避免生命周期混乱
- 需要多次使用同一组数据时,优先保存集合而非流对象
多个流怎么拼?别硬套 concat
Stream.concat 只接受两个参数。拼三个及以上流时,嵌套调用不仅难读,还会产生多余中间流,影响性能:
Stream.concat(Stream.concat(a, b), c) // ❌ 不推荐更清晰、更高效的做法是:
- 用 Stream.of(list1, list2, list3).flatMap(List::stream) —— 支持任意数量,语义直观
- 如果流来源不同(如一个文件流 + 一个 HTTP 响应流),考虑先各自收集再合并,便于错误隔离和日志追踪
- 需要并行拉取多个源头数据时,concat 不适用;应改用 ForkJoinPool 或 CompletableFuture 分别获取再归并
替代方案比拼:concat 还是 flatMap?
两者核心差异不在功能,而在语义与特性:
- concat:强调“缝合”,保持顺序,返回流带 SIZED 标记,适合简单确定性场景
- flatMap:强调“展开”,天然支持多流,但失去大小信息,适合动态或可变数量的合并
- 若需处理可能为空的流(如 Optional.stream()),flatMap 更安全;concat 遇到 null 会直接抛 NPE
- 去重合并时,先各自 distinct 再 concat,比 concat 后整体 distinct 效率高得多
大量免费API接口:立即使用
涵盖生活服务API、金融科技API、企业工商API、等相关的API接口服务。免费API接口可安全、合规地连接上下游,为数据API应用能力赋能!











