为什么在发出终端操作后 Java close() 不流?

Why doesn't Java close() stream after a terminal operation is issued?

阅读 https://www.airpair.com/java/posts/spring-streams-memory-efficiency 后,我很想将结果从数据库中导出,但正如我与一位同事讨论的那样(参见他在那篇文章中添加的评论),需要记住使用 try -with-resources 构造以避免任何内存泄漏。

  1. 为什么 Java 8 库不在每个 terminal operation 之后自行关闭流(而不必将流实例化包装在 try-with-resources 中)?
  2. 如果适用,是否有将此功能添加到 Java 的任何计划,或者请求它是否有意义?

Why doesn't the Java 8 library take care of closing streams itself after each terminal operation (without having to wrap the stream instantiation in a try-with-resources)?

因为终端操作期间或之前可能会出现异常,并且因为您可能不希望终端操作关闭流。如果您确定要关闭流,可以使用 try-with-resource。

If applicable, are there any plans for this functionality to be added to Java, or would it make sense to request it?

这没有意义,请参阅上面的答案。

我认为您将 java.io.InputStreamjava.util.stream.Stream 混为一谈,这是两个非常不同的概念。

try-with-resources 适用于实现 Autoclosable 接口的对象,例如 InputStreams。 InputStreams 表示与 IO.

相关的抽象数据源

java.util.stream.Stream<T> 另一方面,实现了函数式编程的一个概念,它代表了一种动态集合,它不一定是静态构建的,而是可以生成的,因此可能是无限的。

Marko Topolnik(您链接到的文章的作者)在文章中主要做的是建议一种将 IO 来源包装到 java.util.stream.Stream 中的方法。这是一个非常聪明的方法,但 java.util.stream.Stream 通常不用于此目的。

因为它们通常不打算与 IO 一起使用,所以它们没有理由在终端操作后包含 closing


编辑:

在你澄清你实际上没有混淆两者之后(很抱歉这么假设),感谢this answer, I found that your exact example is answered in the documentation of AutoCloseable(强调由我自己添加):

It is possible, and in fact common, for a base class to implement AutoCloseable even though not all of its subclasses or instances will hold releasable resources. For code that must operate in complete generality, or when it is known that the AutoCloseable instance requires resource release, it is recommended to use try-with-resources constructions. However, when using facilities such as Stream that support both I/O-based and non-I/O-based forms, try-with-resources blocks are in general unnecessary when using non-I/O-based forms.

因为需要显式释放资源的流实际上是一种非常不寻常的情况。因此,我们选择不使用仅对 0.01% 的使用有价值的东西来加重所有流执行的负担。

我们使 Stream 可自动关闭,以便您 可以 如果愿意,可以从源中释放资源,但这是我们停止的地方,并且有充分的理由。

这样做不仅会自动给大多数用户增加他们不需要的额外工作负担,而且还会违反一般原则:分配资源的人负责关闭资源。当你打电话给

BufferedReader reader = ...
reader.lines().op().op()...

是打开资源的,不是流库,应该关闭它。事实上,由于在某些资源持有对象上调用访问器方法而关闭流有时会关闭底层对象,因此您可能不希望流为您关闭 BufferedReader —— 您可能希望它通话后保持开放状态。

如果你想关闭资源,这也很简单:

try (BufferedReader reader = ...) {
    reader.lines().op()...
}

您可能正在以特定方式使用流,因此看起来 "obvious" 流应该做什么——但那里的用例比您的多。因此,我们不是为了迎合特定的用例,而是从一般原则来处理它:如果你打开了流,你想关闭它,你自己关闭它,但如果你没有打开它,它就不是你关闭的。