Closing displayed streams - what's the idea?

It is well known that Javadoc talks about the Stream interface:

Streams have a BaseStream.close () method and implement AutoCloseable, but almost all stream instances do not need to be closed after use. As a rule, only streams originating from the IO channel (such as those returned by Files.lines (Path, Charset)) require closing . Most threads are supported by collections, arrays, or generate functions that do not require special resource management. (If a stream requires closure, it can be declared as a resource in try-with-resources.)

Good, but there are methods like flatMapToInt in this interface:

 IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper); 

for which the Javadoc specification says:

Each displayed stream is closed after its contents have been placed in this stream.

So, I didn’t succeed: if IntStream not intended for inputting an IO channel in its source, why is it closed inside this method?

For example, the implementation of ReferencePipeline does this as follows:

 try (IntStream result = mapper.apply(u)) { if (result != null) result.sequential().forEach(downstreamAsInt); } 

A more general question may be: should we take care of closing streams such as IntStream (or its descendants) or not? If not, why care flatMapTo* ?

EDIT @Tunaki provided a very interesting one. But all this is about flatMap , where I agree that we should close the stream in the general case. But my question is about special cases: flatMapToInt , flatMapToLong , etc., Where I do not see the need to close streams.

EDIT-2 @BrianGoetz accesses here because it is his quoted email, so it is in the subject :)

+7
java java-8 java-stream
source share
1 answer

The general rule about processing resources is that the one who is responsible for closing the resource is the one who opened it . The flatMap operation is the only operation in the Stream API that opens the Stream , so this is the only operation that closes it.

Quote from this letter , Brian Goetz said:

To summarize, flatMap() is the only operation that internally closes the stream after it is completed, and for good reason, this is the only case where the stream is effectively opened by the operation itself, and therefore must also be closed by the operation. Any other threads are supposed to be opened by the caller and therefore must be closed by the caller.

The above example is as follows. Consider

 try (Stream<Path> paths = Files.walk(dir)) { Stream<String> stream = paths.flatMap(p -> { try { return Files.lines(p); } catch (IOException e) { throw new UncheckedIOException(e); } }); } 

The method link Files::lines returns Stream<String> lines of the file. When the flat mapping operation is completed, it is expected that the open resource used to read the file will be closed. The question is what? Well, closed by the flattest map, because this is the operation that opened Stream first.

Files.lines returns a stream with a pre-registered handler that closes the underlying BufferedReader . When the flatMap operation is performed, this private handler is called and resources will be correctly released.


The reason this idea is included in the flatMapTo* operation is the same: following the above rule, that every resource allocated by a process must be closed by this process.

Just to show that you can build an IntStream that will have the base resource closed, consider the following stream pipeline, where each path is not mapped to its lines, but by the number of characters in each line.

 try (Stream<Path> paths = Files.walk(dir)) { IntStream stream = paths.flatMapToInt(p -> { try { return Files.lines(p).mapToInt(String::length); } catch (IOException e) { throw new UncheckedIOException(e); } }); } 
+14
source share

All Articles