Do terminal operations on streams close the source? [duplicate]

吃可爱长大的小学妹 提交于 2019-12-30 08:03:15

问题


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:

  1. use try-with-resources as stated in the Files.list JavaDoc
  2. 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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!