Do terminal operations close the stream?
dirPath
contains 200k files. I want to read them one by one and do some processing. The following snippet causes java.nio.file.FileSystemException: dirPath/file-N Too many open files
. Isn't the terminal operation forEach()
supposed to close the open stream (i.e. the open file) before moving to the next one? In other words, do I have to add try-with-resources for the streamed files?
Files.list(dirPath)
.forEach(filePath -> {
Files.lines(filePath).forEach() { ... }
});
Solution 1:
No forEach
does not close the stream (created by Files.list
or Files.lines
). It is documented in the javadoc, for example for Files.list
:
The returned stream encapsulates a Reader. If timely disposal of file system resources is required, the try-with-resources construct should be used to ensure that the stream's close method is invoked after the stream operations are completed.
Solution 2:
A nested forEach
is the wrong tool, in most cases.
The code
Files.list(dirPath).forEach(filePath -> Files.lines(filePath).forEach(line -> { ... });
can and should be replaced by
Files.list(dirPath).flatMap(filePath -> Files.lines(filePath)).forEach(line -> { ... });
or well, since it’s not that easy in this case:
Files.list(dirPath).flatMap(filePath -> {
try { return Files.lines(filePath);}
catch(IOException ex) { throw new UncheckedIOException(ex); }
}).forEach(line -> { });
as a side-effect, you get the following for free:
Stream.flatMap(…)
:
Each mapped stream is closed after its contents have been placed into this stream.
So that’s the preferred solution. Or well, to make it entirely correct:
try(Stream<Path> dirStream = Files.list(dirPath)) {
dirStream.flatMap(filePath -> {
try { return Files.lines(filePath);}
catch(IOException ex) { throw new UncheckedIOException(ex); }
}).forEach(line -> { });
}