2

I have two methods that return length's of strings in a list as follows;

    private Stream<Integer> method1(List<String> list) {
        try (final Stream<String> myStream = list.stream()) {
            return myStream.map(String::length);
        }
    }

and

    private Stream<Integer> method2(List<String> list) {
        try (final Stream<String> myStream = list.stream()) {
            return myStream.map(String::length).flatMap(Stream::of);
        }
    }

when I try to consume the resulting stream of either of the methods with;

    List<Integer> collect = method1(list).collect(Collectors.toList());

or

    List<Integer> collect = method2(list).collect(Collectors.toList());

I get

    Exception in thread "main" java.lang.IllegalStateException: source already consumed or closed

Now I'm aware that using a stream in a try-with-resources block is not common. But in my real code I'm using Stream<Path> paths = Files.walk(Path.of(myPath)) in try-with-resources. And in the documentation of Files.walk(..) method it says

This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directories are closed promptly after the stream's operations have completed.

The code above is just an example to show the problem.

My question is why my streams are closed although I use map and flatMap to return a new stream. Am I wrong to expect that these two methods return new Stream instance hence only the myStream is closed but not the returned one from the map operation? I have a vague understanding of monads but do map and flatMap methods' behaviors mean Stream is not a real monad?

Taha
  • 531
  • 4
  • 21
  • Don't use *try-with-resources* and let the caller handle the closing of streams – Lino Jan 14 '20 at 08:55
  • I'm using try-with-resources for my stream in my method. Then I expect this method to create another stream for method caller to consume. – Taha Jan 14 '20 at 09:37

1 Answers1

1

The stream would be closed once try block is completed.

Because Stream<T> extends BaseStream<T, Stream<T>> and BaseStream extends AutoCloseable

why the Stream instance I created in the block is also closed

Because the new Stream instance created in the block is a chained nested stream of myStream and would be closed immediately after myStream (outer stream) is closed. You can verify it with onClose as below,

privat Stream<Integer> method1(List<String> list) {
    try (final Stream<String> myStream = list.stream()) {
        Stream<Integer> integerStream = myStream.onClose(r)
                .map(String::length).onClose(r1);
        return integerStream;
    }
}
Runnable r = ()->{
    System.out.println("closed main stream...");
};
Runnable r1 = ()->{
    System.out.println("closed map stream...");
};
Vikas
  • 6,868
  • 4
  • 27
  • 41
  • 1
    I'm aware that the stream in try-with-resources would be closed. I'm asking why the `Stream` instance I created in the block is also closed. – Taha Jan 14 '20 at 09:45
  • @Taha updated the answer. Rephrase your question with the above comment, I will vote for reopening the question. – Vikas Jan 14 '20 at 11:24
  • I already did before that comment. Somehow I cannot mention your username. – Taha Jan 14 '20 at 13:25
  • 2
    @Taha there is only one stream. There are only different Stream instances to support fluent building like `stream.map(...).filter(...).distinct()`, whereas each time you chain an operation, you may get a new instance representing the stream, which invalidates the old ones. Since there is only one stream, there is only one "closed" state. – Holger Jan 14 '20 at 16:46
  • Hi guys I wanna ask about this case. In my program it will raise No file descriptors available when I do not use try with resources but when I do try with resources I got the problem with "source already consumed or closed". If I do like this solution will the "integerStream" raise No file descriptors available. Tks – Duc Anh Pham Sep 25 '20 at 03:19