「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。
前言
Hello 大家好,我是l拉不拉米
,流式计算
一直是Java面试中出现频率很高的一个问题,今天『面试の神』系列就来讲一讲 Java 流合并的实现方式。
普通Java方法
JDK 8 Stream 类有一些有用的静态方法。比如concat()
:
合并两个流
@Test
public void merge() {
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()));
}
复制代码
合并多个流
当我们要合并多个流时,操作会变得复杂一些。一个可行的办法是,先合并前两个流,再用合并结果合并其他的流。
比如:
@Test
public void merge() {
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()));
}
复制代码
这种方法对于更多的流变得不可行。当然,我们可以创建中间变量或辅助方法以使其更具可读性,但我们还有更好的办法:
@Test
public void merge() {
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()));
}
复制代码
它经过两个步骤:
- 首先创建一个包含 4 个 Streams 的新 Stream,生成嵌套的流 Stream<Stream>
- 然后我们使用恒等函数将它
flatMap()
转换为 Stream
使用 StreamEx
StreamEx 是一个开源 Java 库,它扩展了 Java 8 Streams 的可能性。它使用 StreamEx 类作为对 JDK 的 Stream 接口的增强。
合并流
StreamEx 库允许我们使用 append()
实例方法合并流:
@Test
public void merge() {
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()));
}
复制代码
由于它是一个实例方法,我们可以轻松地将它链接起来并附加多个 streams。
使用prepend()合并流
StreamEx 还包含一个方法,该方法在另一个之前添加元素,称为 prepend()
:
@Test
public void merge() {
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()));
}
复制代码
使用Jooλ
jooλ是JDK 8兼容库,可为JDK提供有用的扩展。此处最重要的流抽象称为 SEQ
。请注意,这是一个顺序和有序的流,因此调用 parallel() 将没有效果。
合并流
和StreamEx库一样,jooλ也有append()
方法:
@Test
public void merge() {
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()));
}
复制代码
使用prepend()合并流
@Test
public void merge() {
Stream<String> seq = Stream.of("foo", "bar");
Stream<String> openingBracketStream = Stream.of("[");
Stream<String> closingBracketStream = Stream.of("]");
Stream<String> resultingStream = Seq.ofType(seq, String.class)
.append(closingBracketStream)
.prepend(openingBracketStream);
assertEquals(
Arrays.asList("[", "foo", "bar", "]"),
resultingStream.collect(Collectors.toList()));
}
复制代码
总结
可以看出来合并两个流的时候使用 JDK 8 比较简单方便,但当我们需要多个流合并时,使用SteamEx或JOOλ库可以通过append()
的方式追加流,可读性更高。