Stream的javadoc状态:
Streams有一个BaseStream.close()方法并实现AutoCloseable,但几乎所有流实例实际上都不需要在使用后关闭。通常,只有源为IO通道的流(例如Files.lines(Path,Charset)返回的流)才需要关闭。大多数流都由集合,数组或生成函数支持,不需要特殊的资源管理。 (如果流确实需要关闭,则可以在try-with-resources语句中将其声明为资源。)
因此,绝大多数情况下,人们可以在单行中使用Streams,例如collection.stream().forEach(System.out::println);
,但对于Files.lines
和其他资源支持的流,必须使用try-with-resources声明或泄漏资源。
这让我觉得容易出错并且不必要。因为Streams只能迭代一次,所以在我看来,Files.lines
的输出一旦被迭代就不应该被关闭,因此实现应该只是隐式调用close。任何终端操作结束。我错了吗?
答案 0 :(得分:59)
是的,这是一个深思熟虑的决定。我们考虑了两种选择。
这里的操作设计原则是"获得资源的人应该释放资源"。当您阅读EOF时,文件不会自动关闭;我们希望打开文件的人明确关闭文件。由IO资源支持的流是相同的。
幸运的是,该语言提供了一种为您自动执行此操作的机制:try-with-resources。因为Stream实现了AutoCloseable,所以你可以这样做:
try (Stream<String> s = Files.lines(...)) {
s.forEach(...);
}
论证&#34;自动关闭真的很方便,所以我可以把它写成一个单行&#34;很好,但主要是摇尾巴的尾巴。如果您打开了文件或其他资源,则还应该准备关闭它。有效和一致的资源管理胜过&#34;我想在一行中编写这个&#34;,我们选择不扭曲设计只是为了保留单行。
答案 1 :(得分:15)
除了@BrianGoetz之外,我还有更具体的例子。不要忘记Stream
有iterator()
之类的逃生舱口方法。假设你这样做:
Iterator<String> iterator = Files.lines(path).iterator();
之后你可以多次调用hasNext()
和next()
,然后放弃这个迭代器:Iterator
界面完全支持这种使用。无法明确关闭Iterator
,您可以在此处关闭的唯一对象是Stream
。所以这种方式可以完美地运行:
try(Stream<String> stream = Files.lines(path)) {
Iterator<String> iterator = stream.iterator();
// use iterator in any way you want and abandon it at any moment
} // file is correctly closed here.
答案 2 :(得分:4)
另外如果你想要“一行写”。你可以这样做:
Files.readAllLines(source).stream().forEach(...);
如果您确定需要整个文件并且文件很小,则可以使用它。因为它不是懒惰的阅读。
答案 3 :(得分:1)
如果你像我一样懒,并且不介意“如果引发异常,它会使文件句柄保持打开状态”你可以将流包装在一个自动关闭的流中,就像这样(可能还有其他方法) ):
static Stream<String> allLinesCloseAtEnd(String filename) throws IOException {
Stream<String> lines = Files.lines(Paths.get(filename));
Iterator<String> linesIter = lines.iterator();
Iterator it = new Iterator() {
@Override
public boolean hasNext() {
if (!linesIter.hasNext()) {
lines.close(); // auto-close when reach end
return false;
}
return true;
}
@Override
public Object next() {
return linesIter.next();
}
};
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(it, Spliterator.DISTINCT), false);
}