在执行管道的终端操作之前,不会开始遍历管道源。
为什么以下代码抛出:
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上运行。
答案 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;