Written with StackEdit.
问题场景
一天晚上我在查个问题,日常debug代码,某段代码使用了java8 的stream filter,我就手动运行了下这个结果,然后让代码正常运行这句的时候,抛出了异常,异常栈如下:
Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closed
at java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)
at java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)
at java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:618)
at java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:163)
at java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:162)
这让我一度怀疑起了人生,在使用流操作的时候,并没有注意这样的场景和问题。
问题猜想
其实这个问题场景是很明确的,就是同一个流对象运行了一次之后,再次运行就会出问题。
demo复现
java 代码如下:
/**
* 流关闭情况
* @author xiaoming
*/public class StreamClose {
public static void main(String[] args) {
List list = getList(10);
Stream stream = list.stream();
Stream stream1 = stream.filter(n -> (Integer) n > 5);
Stream stream2 = stream.filter(n -> (Integer) n < 5);
System.out.println(stream1.collect(Collectors.toList()).toString());
System.out.println(stream2.collect(Collectors.toList()).toString());
}
private static List getList(int size) {
ArrayList<Integer> list = Lists.newArrayList();
for (int n = 0; n < size; n++) {
list.add(n);
}
return list;
}
}
代码对stream进行了二次filter操作,复现了这个问题
跟踪异常栈
- 跟到filter里面去:
@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) {
@Override
Sink<P_OUT> opWrapSink(int flags, Sink<P_OUT> sink) {
return new Sink.ChainedReference<P_OUT, P_OUT>(sink) {
@Override
public void begin(long size) {
downstream.begin(-1);
}
@Override
public void accept(P_OUT u) {
if (predicate.test(u))
downstream.accept(u);
}
};
}
};
}
创建了一个StatelessOp<P_OUT, P_OUT>对象
2. 继续跟踪异常栈到了StatelessOp<P_OUT, P_OUT>的构造函数里,代码如下:
/**
* Construct a new Stream by appending a stateless intermediate * operation to an existing stream. * * @param upstream The upstream pipeline stage
* @param inputShape The stream shape for the upstream pipeline stage
* @param opFlags Operation flags for the new stage
*/StatelessOp(AbstractPipeline<?, E_IN, ?> upstream,
StreamShape inputShape,
int opFlags) {
super(upstream, opFlags);
assert upstream.getOutputShape() == inputShape;
}
- 跟踪super的构造函数,发现AbstractPipeline抽象类的构造方法如下:
/**
* Constructor for appending an intermediate operation stage onto an * existing pipeline. * * @param previousStage the upstream pipeline stage
* @param opFlags the operation flags for the new stage, described in
* {@link StreamOpFlag}
*/AbstractPipeline(AbstractPipeline<?, E_IN, ?> previousStage, int opFlags) {
if (previousStage.linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
previousStage.linkedOrConsumed = true;
previousStage.nextStage = this;
this.previousStage = previousStage;
this.sourceOrOpFlags = opFlags & StreamOpFlag.OP_MASK;
this.combinedFlags = StreamOpFlag.combineOpFlags(opFlags, previousStage.combinedFlags);
this.sourceStage = previousStage.sourceStage;
if (opIsStateful())
sourceStage.sourceAnyStateful = true;
this.depth = previousStage.depth + 1;
}
看代码不难发现,我们对流的状态进行了判断和修改
if (previousStage.linkedOrConsumed)
throw new IllegalStateException(MSG_STREAM_LINKED);
previousStage.linkedOrConsumed = true;
previousStage.nextStage = this;
所以再次运行的时候,流的状态 :previousStage.linkedOrConsumed = true,然后就会抛出异常
思考
流为什么要关闭呢?其实想想是应该的,流不应该保留之前的数据,不然对于内存来讲其实是很不友好,而且也没有必要