问题
Consider the following code:
Path directory = Paths.get(/* some directory */);
Files.list(directory).forEach(System.out::println);
Does a terminal operation (like forEach
) close the underlying file that has been opened?
Refer to the relevant parts of the javadoc of Files.list:
The returned stream encapsulates a DirectoryStream. 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.
If it doesn't call Stream.close()
, what would then be the best alternative to call it while producing maintainable code?
回答1:
Terminal operators do NOT close the stream automatically. Consider this code:
Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Closed"));
list.forEach(System.out::println);
This does NOT print "Closed".
However, the following does print "Closed":
try (Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Closed"))) {
list.forEach(System.out::println);
}
So the best way to do it is to use the try-with-resources mechanism.
回答2:
So a quick-check reveals that forEach
does not close the DirectoryStream
:
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.function.Consumer;
import java.util.stream.Stream;
/**
* Created for http://stackoverflow.com/q/27381329/1266906
*/
public class FileList {
public static void main(String[] args) {
Path directory = Paths.get("C:\\");
try {
Stream<Path> list = Files.list(directory).onClose(() -> System.out.println("Close called"));
list.forEach(System.out::println);
// Next Line throws "java.lang.IllegalStateException: stream has already been operated upon or closed" even though "Close called" was not printed
list.forEach(System.out::println);
} catch (IOException | IllegalStateException e) {
e.printStackTrace(); // TODO: implement catch
}
// The mentioned try-with-resources construct
try (Stream<Path> list = Files.list(directory)) {
list.forEach(System.out::println);
} catch (IOException | IllegalStateException e) {
e.printStackTrace(); // TODO: implement catch
}
// Own helper-method
try {
forEachThenClose(Files.list(directory), System.out::println);
} catch (IOException | IllegalStateException e) {
e.printStackTrace(); // TODO: implement catch
}
}
public static <T> void forEachThenClose(Stream<T> list, Consumer<T> action) {
try {
list.forEach(action);
} finally {
list.close();
}
}
}
I see the two presented mitigations:
- use try-with-resources as stated in the
Files.list
JavaDoc - write your own helper-method which utilizes a finally-block
Which is more maintainable depends probably on how-many helper-methods you would need.
来源:https://stackoverflow.com/questions/27381329/do-terminal-operations-on-streams-close-the-source