Java 8 Stream - 过滤和foreach方法不按预期打印

时间:2016-11-29 10:40:26

标签: java java-8 java-stream

我正在执行以下程序:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
    System.out.println("filter: " + s);
    return true;
})
.forEach(s -> System.out.println("forEach: " + s));

我得到的输出是:

filter:  d2
forEach: d2
filter:  a2
forEach: a2
filter:  b1
forEach: b1
filter:  b3
forEach: b3
filter:  c
forEach: c

但是,我期待以下输出:

filter:  d2
filter:  a2
filter:  b1
filter:  b3
filter:  c 
forEach: d2
forEach: a2
forEach: b1
forEach: b3
forEach: c

意思是,首先filter方法循环应该已经完全执行,然后forEach方法循环应该已经开始。

我有什么问题吗?

2 个答案:

答案 0 :(得分:8)

你的期望是错误的。

执行终端操作forEach时,它一次消耗Stream的一个元素。它消耗的每个元素都必须传递Stream的所有中间操作,这导致在filter在同一元素上执行之前的元素上执行forEach(假设元素通过filter)。

换句话说,当filter管道中的下一个操作需要其下一个元素(在您的情况下,下一个操作是Stream)时,forEach会被懒惰地应用于每个元素。

这意味着如果您的终端操作只需要处理某些Stream元素(例如,如果您将forEach()替换为findFirst()),则filter()操作只会执行直到第一个元素通过它(在你的例子中意味着只对第一个元素执行过滤器)。

答案 1 :(得分:1)

Stream的处理将从终端操作开始,并逐个使用一个项目。优点是:

该计划必须计算仅相关项 - 例如,如果只需要一个项目,则filter仅被调用一次:

Stream.of("d2", "a2", "b1", "b3", "c")
.filter(s -> {
    System.out.println("filter: " + s);
    return true;
})
.findAny()
.ifPresent(s -> System.out.println("forEach: " + s));

输出:

filter: d2
forEach: d2

你甚至可以创建 infinte Streams(否则这是不可能的):

IntStream.iterate(0, i -> i+1)
.filter(s -> {
    System.out.println("filter: " + s);
    return true;
})
.peek(i -> {
    try {
        Thread.sleep(1000);
    } catch (InterruptedException e) {
    }
})
.forEach(s -> System.out.println("forEach: " + s));

输出:

filter: 0
forEach: 0
filter: 1
forEach: 1
... (up to infinity)