为什么这个java Stream会运行两次?

时间:2016-01-08 12:59:02

标签: java java-8 java-stream

Java 8 API说:

  

在执行管道的终端操作之前,不会开始遍历管道源。

为什么以下代码抛出:

  

java.lang.IllegalStateException:stream已被操作   或关闭

Stream<Integer> stream = Stream.of(1,2,3);
stream.filter( x-> x>1 );
stream.filter( x-> x>2 ).forEach(System.out::print);

根据API的第一个过滤操作不应该在Stream上运行。

4 个答案:

答案 0 :(得分:27)

这是因为您忽略了filter的返回值。

Stream<Integer> stream = Stream.of(1,2,3);
stream.filter( x-> x>1 ); // <-- ignoring the return value here
stream.filter( x-> x>2 ).forEach(System.out::print);

Stream.filter返回 new Stream,其中包含与给定谓词匹配的此流的元素。但重要的是要注意它是一个新流。当过滤器加入过滤器时,旧的操作已经进行。但新的不是。

引自Stream Javadoc:

  

应该只对一个流进行操作(调用中间或终端流操作)。

在这种情况下,filter是对旧Stream实例进行操作的中间操作。

所以这段代码可以正常工作:

Stream<Integer> stream = Stream.of(1,2,3);
stream = stream.filter( x-> x>1 ); // <-- NOT ignoring the return value here
stream.filter( x-> x>2 ).forEach(System.out::print);

正如Brian Goetz所说,你通常将这些电话联系在一起:

Stream.of(1,2,3).filter( x-> x>1 )
                .filter( x-> x>2 )
                .forEach(System.out::print);

答案 1 :(得分:3)

filter()方法使用流并返回您在示例中忽略的另一个Stream实例。

filter是一个中间操作,但您不能在同一个流实例上调用过滤器

您的代码应写如下:

Stream<Integer> stream = Stream.of(1,2,3);
                               .filter( x-> x>1 )
                               .filter( x-> x>2);
stream.forEach(System.out::print);

由于过滤器是一个中间操作,因此在调用这些方法时“无需”。调用forEach()方法

时,所有工作都已得到处理

答案 2 :(得分:1)

Streams上的文档说:

&#34;应该只对一个流进行操作(调用中间或终端流操作)。&#34;

您实际上可以在源代码中看到这一点。当你调用filter时,它会返回一个新的无状态操作,在构造函数(this)中传递当前的管道实例:

@Override
public final Stream<P_OUT> filter(Predicate<? super P_OUT> predicate) {
    Objects.requireNonNull(predicate);
    return new StatelessOp<P_OUT, P_OUT>(this, StreamShape.REFERENCE,
                                       StreamOpFlag.NOT_SIZED) {
        ....
}

构造函数调用最终调用AbstractPipeline构造函数,其构造方式如下:

AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
    if (previousStage.linkedOrConsumed)
        throw new IllegalStateException(MSG_STREAM_LINKED);
    previousStage.linkedOrConsumed = true;
    ...
}

第一次在源(第2行)上调用filter时,它将布尔值设置为true。由于您没有重复使用filter提供的返回值,第二次调用filter(第3行)将检测到原始流源(第1行)已经链接(由于第一次过滤调用),因此你得到的例外。

答案 3 :(得分:0)

这是对stream的误用,当您向其添加多个.fliter()时会检测到这种情况。

它并没有说它已经被多次遍历,只是它已经被操作过了&#34;