使用StreamSupplier的IllegalStateException

时间:2014-12-02 10:34:45

标签: java java-8 illegalstateexception optional java-stream

我有以下代码在一个流中执行不同的操作。

  private void getBuildInformation(Stream<String> lines)
  {
    Supplier<Stream<String>> streamSupplier = () -> lines;

    String buildNumber = null;
    String scmRevision = null;
    String timestamp = null;
    String buildTag = null;

      Optional<String> hasBuildNumber = streamSupplier.get().filter(s -> s.contains(LogProps.PLM_BUILD)).findFirst();
      if (hasBuildNumber.isPresent())
      {
        buildNumber = hasBuildNumber.get();
        String[] temp = buildNumber.split("=");
        if (temp.length >= 2)
          buildNumber = temp[1].trim();
      }

      Optional<String> hasSCMRevision = streamSupplier.get().filter(s -> s.contains(LogProps.SCM_REVISION_50)).findFirst();
      if (hasSCMRevision.isPresent())
      {
        scmRevision = hasSCMRevision.get();
        String[] temp = scmRevision.split(":");
        if (temp.length >= 4)
          scmRevision = temp[3].trim();
      }

      Optional<String> hasBuildTag = streamSupplier.get().filter(s -> s.contains(LogProps.BUILD_TAG_50)).findFirst();
      if (hasBuildTag.isPresent())
      {
        buildTag = hasBuildTag.get();
        String[] temp = buildTag.split(":");
        if (temp.length >= 4)
          buildTag = temp[3].trim();
      }

      Optional<String> hasTimestamp = streamSupplier.get().filter(s -> s.contains(LogProps.BUILD_TIMESTAMP_50)).findFirst();
      if (hasTimestamp.isPresent())
      {
        timestamp = hasTimestamp.get();
        String[] temp = timestamp.split(":");
        if (temp.length >= 4)
          timestamp = temp[3].trim();
      }
}

现在的问题是,如果我第一次打电话

Optional<String> hasBuildNumber = streamSupplier.get().filter(s -> s.contains(LogProps.PLM_BUILD)).findFirst();

它工作正常,但如果我打电话给下一个

Optional<String> hasSCMRevision = streamSupplier.get().filter(s -> s.contains(LogProps.SCM_REVISION_50)).findFirst();

我得到以下异常:

Exception in thread "Thread-21" 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)
    at com.dscsag.dscxps.model.analysis.Analysis.getECTRBuildInformation(Analysis.java:205)
    at com.dscsag.dscxps.model.analysis.Analysis.parseLogFile(Analysis.java:153)
    at com.dscsag.dscxps.model.analysis.Analysis.analyze(Analysis.java:135)
    at com.dscsag.dscxps.model.XPSModel.lambda$startAnalysis$0(XPSModel.java:467)
    at com.dscsag.dscxps.model.XPSModel$$Lambda$1/12538894.run(Unknown Source)
    at java.lang.Thread.run(Thread.java:745)

自从我阅读此页http://winterbe.com/posts/2014/07/31/java8-stream-tutorial-examples/后,我认为它应该有效,因为供应商会在get()上提供新的流。

1 个答案:

答案 0 :(得分:4)

如果您将供应商重新编写为匿名的pre-java 8类。这相当于:

Supplier<Stream<String>> streamSupplier = new Supplier<Stream<String>>() {
     @Override
     public Stream<String> get() {
         return lines;
     }
 };

也许在这里,每次在供应商处调用get时都会返回相同的流实例(因此,findFirst是{{3 }})。你没有返回一个全新的Stream。

在您提供的网页示例中,作者使用Stream.of每次调用get时都会创建一个全新的流,这就是它工作的原因。

AFAIK无法从现有流复制流。因此,一种解决方法是传递Stream进入的对象,然后在供应商中获取Stream。

public class Test {
    public static void main(String[] args) {    
        getBuildInformation(Arrays.asList("TEST", "test"));
    }
    private static void getBuildInformation(List<String> lines) {
        Supplier<Stream<String>> streamSupplier = () -> lines.stream();

        Optional<String> hasBuildNumber = streamSupplier.get().filter(s -> s.contains("t")).findFirst();
        System.out.println(hasBuildNumber);

        Optional<String> hasSCMRevision = streamSupplier.get().filter(s -> s.contains("T")).findFirst();
        System.out.println(hasSCMRevision);

    }
}

哪个输出:

Optional[test]
Optional[TEST]

由于您从Path对象获取行,因此在供应商本身处理异常会非常难看,因此您可以创建一个帮助方法来处理要捕获的异常,那么它就像这样:

private static void getBuildInformation(Path path) {
    Supplier<Stream<String>> streamSupplier = () -> lines(path);
    //do your stuff
}
private static Stream<String> lines(Path path) {
    try { 
        return Files.lines(path); 
    }
    catch (IOException e) { 
        throw new UncheckedIOException(e); 
    }
}