17

stream.spliterator()隐式关闭stream,还是需要在之后显式关闭它?

Stream<String> stream = Stream.of("a", "b", "c");
Spliterator<T> spliterator = stream.spliterator();
// Some low lever operation with the spliterator
stream.close(); // do we need to close?

乍一看,该.spliterator()方法似乎关闭了stream,但没有调用stream.close()。至少如果我在.spliterator()调用该方法后立即关闭它,它似乎不会影响拆分器操作。

Stream<String> stream = Stream.of("a", "b", "c").limit(2);
Spliterator<T> spliterator = stream.spliterator();
stream.close();
// Some low lever operation with the spliterator

这个问题可以扩展到其他stream方法,例如.findAny().

stream.findAny() // Can I assume that I don't need to close the stream?
stream.onClose(() -> System.out.println("hi!")).findAny()`
// when the `onClose()` action will be called?

提出这个问题的原因是要明确何时stream需要明确关闭 a ,以及在我不需要明确关闭它的情况下,onClose()定义的操作何时发生?

4

4 回答 4

12

终端操作永远不会关闭流。关闭必须手动完成。唯一发生自动关闭的地方是在flatMap操作中,手动关闭通常在运行中创建的子流将是困难和不可能之间的某个地方。

这也适用于该Stream.spliterator()方法。在您的示例中,这没有区别,因为通过创建的流Stream.of(…)不需要关闭并且onClose()默认情况下没有注册任何操作。

您必须查阅工厂方法的文档以了解何时需要关闭流。比如喜欢Files#lines(Path, Charset)

另请参阅Stream 上的 collect 操作是否会关闭流和底层资源?还是Java 8Stream.iterator()完成后会自动关闭流?

于 2017-10-11T13:15:42.630 回答
5

在 Java 9中关闭 s 没有任何改变。Stream如果应该释放底层资源,您仍然需要手动执行此操作。你永远不应该依赖垃圾收集器来完成它。文档仍然说:

流有一个BaseStream.close()方法和实现AutoCloseable。在关闭后对流进行操作将抛出IllegalStateException. 大多数流实例在使用后实际上不需要关闭,因为它们由集合、数组或生成函数支持,不需要特殊的资源管理。通常,只有源为 IO 通道的流(例如由 返回的流)才Files.lines(Path)需要关闭。如果流确实需要关闭,则必须在 try-with-resources 语句或类似的控制结构中将其作为资源打开,以确保在其操作完成后立即关闭。

于 2017-10-11T13:13:56.757 回答
4

对方法的调用spliterator()返回Spliterator此流的元素的a 及其终端操作。

回答您的问题 - 不,该spliterator方法或为此目的,其他任何终端操作都不会关闭流。

这代表终端操作记录为 -

终端操作执行后,流管道被认为已消耗,不能再使用....在几乎所有情况下,终端操作都是急切的,在返回之前完成它们对数据源的遍历和管道的处理。只有终端操作iterator()spliterator()不是;这些是作为“逃生舱”提供的,以在现有操作不足以完成任务的情况下启用任意客户端控制的管道遍历。

Stream另一方面,在关闭文档时,文档指出:-

大多数流实例在使用后实际上不需要关闭,因为它们由集合、数组或生成函数支持,不需要特殊的资源管理。通常,只有源为 IO 通道的流(例如由 返回的流)才Files.lines(Path)需要关闭。


与之匹配的AutoCloseable州-

即使不是所有的子类或实例都拥有可释放的资源,基类也有可能实现 AutoCloseable,实际上这很常见。

这就是 BaseStream 扩展它的方式,并且close()不会比使用资源(例如Files.lines(...).

但是,当使用支持基于 I/O 和非基于 I/O 的表单的 Stream 等设施时,在使用非基于 I/O 的表单时通常不需要 try-with-resources 块。

于 2017-10-11T13:16:15.720 回答
0

该方法stream.spliterator()不关闭Stream,就像没有其他终端操作一样。您可能知道也可能不知道,如果您需要关闭Stream,这仍然是一个很大的争论。我个人希望拥有所有终端操作spliterator(),但出于各种原因iterator()隐式关闭。Stream这样做并没有什么坏处,因为大多数实现只是什么都不做。

您可以在java.util.stream.AbstractPipeline. 调用时添加的关闭动作stream.onClose(action)会在调用的那一刻stream.close()被调用。

我无法告诉您何时需要Stream明确关闭 a,因为当前的策略是在返回 的方法中记录它Stream,例如Files.list(Path)

返回的流包含对打开目录的引用。通过关闭流来关闭目录。...

此方法必须在 try-with-resources 语句或类似的控制结构中使用,以确保在流的操作完成后立即关闭流的打开目录。

当你调用stream.spliterator()时,返回的Spliterator可能是惰性的(另见AbstractPipeline.lazySpliterator(..)),因此仍然需要对原始的元素进行操作Stream。如果您Stream在遍历 的元素之前关闭Spliterator,您可能会遇到异常或Spliterator根本不再操作。

public static void main(final String[] args) throws IOException {
    try (final Stream<Path> stream = Files.list(Paths.get(""))) {
        stream.onClose(() -> System.out.println("stream closed"));
        final Spliterator<Path> split = stream.spliterator();

        /*
         * uncomment this to see what happens when you close the
         * stream before you traverse the spliterator
         */
        // stream.close();

        split.forEachRemaining(System.out::println);
    }
}

现在的问题是,如果你想要一个返回 a 的方法,Spliterator调用者没有close()可以在Spliterator.

于 2019-01-09T19:47:18.147 回答