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); } }); }
Tunaki
source share