流<Stream>:flatMap与reduce

JB *_*zet 10 java java-8 java-stream

如果我执行以下代码"连接"两个流

  • 首先通过flatMapping a Stream<Stream<Integer>>
  • 然后通过减少Stream<Stream<Integer>>使用Stream.concat()

在两种情况下我都获得了相同的正确结果,但过滤操作的数量是不同的.

public class FlatMapVsReduce {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);

        Predicate<Integer> predicate1 = i -> {
            System.out.println("testing first condition with " + i);
            return i == 3;
        };

        Predicate<Integer> predicate2 = i -> {
            System.out.println("testing second condition with " + i);
            return i == 7;
        };

        System.out.println("Testing with flatMap");
        Integer result1 =
            Stream.of(list.stream().filter(predicate1),
                      list.stream().filter(predicate2))
                  .flatMap(Function.identity())
                  .peek(i -> System.out.println("peeking " + i))
                  .findFirst()
                  .orElse(null);
        System.out.println("result1 = " + result1);

        System.out.println();
        System.out.println("Testing with reduce");
        Integer result2 =
            Stream.of(list.stream().filter(predicate1),
                      list.stream().filter(predicate2))
                  .reduce(Stream::concat)
                  .orElseGet(Stream::empty)
                  .peek(i -> System.out.println("peeking " + i))
                  .findFirst()
                  .orElse(null);
        System.out.println("result2 = " + result2);
    }
}
Run Code Online (Sandbox Code Playgroud)

我在两种情况下都得到了预期的结果(3).但是,第一个操作对集合的每个元素应用第一个过滤器,而第二个过滤器在满足一个元素时立即停止.输出是:

Testing with flatMap
testing first condition with 1
testing first condition with 2
testing first condition with 3
peeking 3
testing first condition with 4
testing first condition with 5
testing first condition with 6
testing first condition with 7
testing first condition with 8
testing first condition with 9
result1 = 3

Testing with reduce
testing first condition with 1
testing first condition with 2
testing first condition with 3
peeking 3
result2 = 3
Run Code Online (Sandbox Code Playgroud)

为什么两者之间的行为存在差异?JDK代码是否可以在第一个场景中提高效率而不是在第二个场景中有效,或者flatMap中是否存在使其无法实现的内容?

附录:以下替代方案与使用reduce的方法一样有效,但我仍无法解释原因:

    Integer result3 = Stream.of(predicate1, predicate2)
                            .flatMap(c -> list.stream().filter(c).limit(1))
                            .peek(i -> System.out.println("peeking " + i))
                            .findFirst()
                            .orElse(null);
    System.out.println("result3 = " + result3);
Run Code Online (Sandbox Code Playgroud)

njz*_*zk2 5

从openJDK 中 flatMap的实现来看,我的理解是将flatMap传入流的全部内容推送到下游:

result.sequential().forEach(downstreamAsInt);
Run Code Online (Sandbox Code Playgroud)

另一方面,Stream::concat似乎正在处理拉动,而不是立即发送所有内容。

我怀疑你的测试没有显示全貌:

  • 在 中flatMap,仅当第一个流耗尽时才考虑第二个流。
  • 在 中reduce,所有流都被推送到最终的串联流中,因为在消耗完输入流的所有内容之前,缩减的对象没有意义。

这意味着使用其中之一取决于您的输入的复杂程度。如果你有一个无限的Stream<Stream<Integer>>,reduce将永远不会完成。

  • 如果我有无限整数流的有限流,@YassinHajaj flatMap 永远不会完成。如果我有有限整数流的无限流,则减少永远不会完成。 (4认同)