java8 stream close问题

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操作,复现了这个问题

跟踪异常栈

  1. 跟到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;  
}
  1. 跟踪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,然后就会抛出异常

思考

流为什么要关闭呢?其实想想是应该的,流不应该保留之前的数据,不然对于内存来讲其实是很不友好,而且也没有必要

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值