为什么这个Java Stream运行两次?

Java 8 API说:

直到stream水线的terminal操作被执行,才开始stream水线源的穿越。

那么为什么下面的代码抛出:

java.lang.IllegalStateException:stream已经被操作或closures

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

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

发生这种情况是因为您忽略了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返回一个新的 Stream它由与给定的谓词相匹配的这个stream的元素组成。 但重要的是要注意这是一个新的stream。 当filter被添加时,旧的已经被操作。 但新的不是。

引用Stream Javadoc:

应该只对一个数据stream进行操作(调用中间或terminalstream操作)一次。

在这种情况下, 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); 

filter()方法使用这个stream,并返回一个你在例子中忽略的其他Stream实例。

filter是一个中间操作,但不能在同一个stream实例上调用两次filter

你的代码应该写成如下:

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

由于filter是一个中间操作,所以在调用这些方法时,“无”就完成了。 当调用forEach()方法时,所有的工作都被处理

Streams上的文档说:

“一个stream只能运行一次(调用中间stream或terminalstream操作)。”

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

 @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行)将检测到原始stream源(第1行)已经链接(由于第一个filter调用),因此您得到。

这是一个误用的stream ,当你连接多个.fliter() ,它会被检测到。

它并不是说它已经被遍历过一次以上,只是它已经“已经被操作了”