Java中调用peek后执行forEach抛出流已操作异常的原因咨询
Java Stream调用peek后执行forEach抛出异常的原因解析
先看这段代码:
Stream<Integer> ints = Stream.of(1, 2, 3); ints.peek(System.out::println); ints.forEach(System.out::println);
运行后会抛出异常:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
核心疑问
- peek是中间操作,不是说只有终端操作才会触发流的执行吗?为什么执行forEach时流已经被标记为已操作?
- 原本以为peek会像String的
toUpperCase()那样,因为没使用返回的新对象就被忽略,比如这个例子:
String str = "hello world"; str.toUpperCase(); str.charAt(0); // <-- 结果是h不是H
问题根源
Java Stream和String的设计逻辑完全不同:
- String是不可变类:调用
toUpperCase()会生成一个新的String对象,但原String实例不受任何影响,依然可以正常使用。 - Stream是一次性实例:任何对Stream实例的操作(不管是中间操作还是终端操作),都会把这个实例标记为"已操作/已关闭",后续不能再用它调用任何操作。
你代码里的ints.peek(...),虽然你没有接收它返回的新Stream,但这个调用已经修改了原流ints的状态——把它标记为已操作。当你后续再用ints调用forEach时,自然就会触发IllegalStateException。
正确写法
必须使用peek返回的新Stream来执行后续操作,比如链式调用:
Stream.of(1, 2, 3) .peek(System.out::println) .forEach(System.out::println);
或者先接收新流再操作:
Stream<Integer> ints = Stream.of(1, 2, 3); Stream<Integer> newInts = ints.peek(System.out::println); newInts.forEach(System.out::println);
这样操作的是新生成的Stream实例,不会出现重复使用原流的问题。
内容的提问来源于stack exchange,提问作者mrbela
相关产品推荐
相关产品推荐

