Завершают ли терминальные операции поток?
dirPath
содержит 200 тыс. файлов. Я хочу прочитать их один за другим и сделать некоторые обработки. Следующий фрагмент вызывает java.nio.file.FileSystemException: dirPath/file-N Too many open files
. Разве терминальная операция forEach()
должна закрывать открытый поток (т.е. Открытый файл) перед переходом к следующему? Другими словами, мне нужно добавить try-in-resources для потоковых файлов?
Files.list(dirPath)
.forEach(filePath -> {
Files.lines(filePath).forEach() { ... }
});
Ответы
Ответ 1
Нет forEach
не закрывает поток (созданный Files.list
или Files.lines
). Он задокументирован в javadoc, например, для Files.list
:
Возвращаемый поток инкапсулирует Reader. Если требуется своевременное удаление ресурсов файловой системы, следует использовать конструкцию try-with-resources для обеспечения того, чтобы метод закрытия потока был вызван после завершения операций потока.
Ответ 2
Вложенные forEach
в большинстве случаев являются неправильным инструментом.
Код
Files.list(dirPath).forEach(filePath -> Files.lines(filePath).forEach(line -> { ... });
может и должен быть заменен на
Files.list(dirPath).flatMap(filePath -> Files.lines(filePath)).forEach(line -> { ... });
или хорошо, поскольку в этом случае это не так просто:
Files.list(dirPath).flatMap(filePath -> {
try { return Files.lines(filePath);}
catch(IOException ex) { throw new UncheckedIOException(ex); }
}).forEach(line -> { });
как побочный эффект, вы получаете следующее бесплатно:
Каждый отображаемый поток закрывается после того, как его содержимое было помещено в этот поток.
Итак, это предпочтительное решение. Или хорошо, чтобы сделать его полностью правильным:
try(Stream<Path> dirStream = Files.list(dirPath)) {
dirStream.flatMap(filePath -> {
try { return Files.lines(filePath);}
catch(IOException ex) { throw new UncheckedIOException(ex); }
}).forEach(line -> { });
}