1. 概述
本文将深入探讨 Java 中合并 Stream 的多种实现方式。Stream 合并操作虽然基础,但实际应用中容易踩坑,需要特别注意实现细节。
2. 使用原生 Java 实现
JDK 8 的 Stream 类提供了一些实用的静态工具方法,我们重点分析 concat()
方法的使用场景。
2.1 合并两个 Stream
最简单的合并方式是使用静态方法 Stream.concat()
:
@Test
public void whenMergingStreams_thenResultStreamContainsElementsFromBoth() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> resultingStream = Stream.concat(stream1, stream2);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6),
resultingStream.collect(Collectors.toList()));
}
2.2 合并多个 Stream
当需要合并超过两个 Stream 时,操作会变得复杂。以下是几种实现方式:
方案一:嵌套调用(不推荐)
@Test
public void given3Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> stream3 = Stream.of(18, 15, 36);
Stream<Integer> resultingStream = Stream.concat(
Stream.concat(stream1, stream2), stream3);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36),
resultingStream.collect(Collectors.toList()));
}
⚠️ 这种方式在 Stream 数量较多时会导致代码可读性急剧下降
方案二:flatMap 优化(推荐)
@Test
public void given4Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> stream3 = Stream.of(18, 15, 36);
Stream<Integer> stream4 = Stream.of(99);
Stream<Integer> resultingStream = Stream.of(
stream1, stream2, stream3, stream4)
.flatMap(i -> i);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36, 99),
resultingStream.collect(Collectors.toList()));
}
✅ 实现原理:
- 先创建包含多个 Stream 的 Stream(
Stream<Stream<Integer>>
) - 通过
flatMap()
扁平化为单层 Stream
3. 使用 StreamEx 库
StreamEx 是增强 Java 8 Stream 功能的开源库,提供更便捷的 API。
3.1 基础合并操作
使用 append()
实例方法链式合并:
@Test
public void given4Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> stream1 = Stream.of(1, 3, 5);
Stream<Integer> stream2 = Stream.of(2, 4, 6);
Stream<Integer> stream3 = Stream.of(18, 15, 36);
Stream<Integer> stream4 = Stream.of(99);
Stream<Integer> resultingStream = StreamEx.of(stream1)
.append(stream2)
.append(stream3)
.append(stream4);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6, 18, 15, 36, 99),
resultingStream.collect(Collectors.toList()));
}
💡 提示:若将 resultingStream
声明为 StreamEx
类型,可直接调用 toList()
简化收集操作
3.2 前置合并操作
使用 prepend()
在 Stream 前部插入元素:
@Test
public void given3Streams_whenPrepended_thenResultStreamContainsAllElements() {
Stream<String> stream1 = Stream.of("foo", "bar");
Stream<String> openingBracketStream = Stream.of("[");
Stream<String> closingBracketStream = Stream.of("]");
Stream<String> resultingStream = StreamEx.of(stream1)
.append(closingBracketStream)
.prepend(openingBracketStream);
assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
4. 使用 jOOλ 库
jOOλ 是兼容 JDK 8 的增强库,核心抽象是 Seq
接口(有序顺序流)。
4.1 基础合并操作
同样提供 append()
方法:
@Test
public void given2Streams_whenMerged_thenResultStreamContainsAllElements() {
Stream<Integer> seq1 = Stream.of(1, 3, 5);
Stream<Integer> seq2 = Stream.of(2, 4, 6);
Stream<Integer> resultingSeq = Seq.ofType(seq1, Integer.class)
.append(seq2);
assertEquals(
Arrays.asList(1, 3, 5, 2, 4, 6),
resultingSeq.collect(Collectors.toList()));
}
💡 提示:将 resultingSeq
声明为 Seq
类型可使用便捷的 toList()
方法
4.2 前置合并操作
prepend()
方法实现前插操作:
@Test
public void given3Streams_whenPrepending_thenResultStreamContainsAllElements() {
Stream<String> seq = Stream.of("foo", "bar");
Stream<String> openingBracketSeq = Stream.of("[");
Stream<String> closingBracketSeq = Stream.of("]");
Stream<String> resultingStream = Seq.ofType(seq, String.class)
.append(closingBracketSeq)
.prepend(openingBracketSeq);
Assert.assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
5. 总结
Stream 合并操作的实现方式对比:
方案 | 适用场景 | 优势 | 劣势 |
---|---|---|---|
原生 Java | 简单合并 | 无需依赖 | 多流合并代码冗长 |
StreamEx | 复杂链式操作 | API 友好 | 需引入第三方库 |
jOOλ | 需要顺序保证 | 类型安全增强 | 并行流支持有限 |
✅ 选择建议:
- 简单场景直接使用
Stream.concat()
- 复杂合并逻辑推荐 StreamEx 或 jOOλ
- 注意 Stream 的消费特性(合并后原流不可复用)