『面试の神』Java怎样实现流合并?

「这是我参与11月更文挑战的第21天,活动详情查看:2021最后一次更文挑战」。

前言

Hello 大家好,我是l拉不拉米流式计算一直是Java面试中出现频率很高的一个问题,今天『面试の神』系列就来讲一讲 Java 流合并的实现方式。

普通Java方法

JDK 8 Stream 类有一些有用的静态方法。比如concat():

合并两个流

1
2
3
4
5
6
7
8
9
10
11
java复制代码@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()));
}

合并多个流

当我们要合并多个流时,操作会变得复杂一些。一个可行的办法是,先合并前两个流,再用合并结果合并其他的流。

比如:

1
2
3
4
5
6
7
8
9
10
11
12
13
java复制代码@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()));
}

这种方法对于更多的流变得不可行。当然,我们可以创建中间变量或辅助方法以使其更具可读性,但我们还有更好的办法:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
java复制代码@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
  • 然后我们使用恒等函数将它 flatMap() 转换为 Stream

使用 StreamEx

StreamEx 是一个开源 Java 库,它扩展了 Java 8 Streams 的可能性。它使用 StreamEx 类作为对 JDK 的 Stream 接口的增强。

合并流

StreamEx 库允许我们使用 append() 实例方法合并流:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
java复制代码@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()

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码@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()方法:

1
2
3
4
5
6
7
8
9
10
11
12
java复制代码@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()合并流

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码@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()的方式追加流,可读性更高。

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%