为什么这个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()
,它会被检测到。
它并不是说它已经被遍历过一次以上,只是它已经“已经被操作了”