stream.spliterator()是否关闭了流?

时间:2017-10-11 12:59:13

标签: java java-8 java-stream spliterator

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需要明确关闭时,以及在我不需要明确关闭它的情况下,onClose()定义的操作会发生什么?

4 个答案:

答案 0 :(得分:10)

终端操作从不关闭流。关闭必须手动完成。自动关闭发生的唯一地方是flatMap操作,其中通常在运行中创建的子流的手动关闭将介于艰难和不可能之间。

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

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

另请参阅Does collect operation on Stream close the stream and underlying resources?Does Java 8 Stream.iterator() auto-close the stream when it's done?

答案 1 :(得分:4)

关于Java 9中Stream的关闭没有任何改变。如果应该释放底层资源,你仍然需要手动执行它。你永远不应该依赖垃圾收集器来做到这一点。 docs仍然说:

  

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

答案 2 :(得分:4)

spliterator()方法的调用会为此流的元素返回 Spliterator ,并其终端操作。

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

记录为terminal operations作为 -

  

执行终端操作后,流管道为   考虑消耗,不能再使用....几乎在所有情况下,终端操作    渴望 ,完成对数据源的遍历和   在返回之前处理管道。只有终端   操作iterator()spliterator(不是;这些是作为   一个“逃生舱口”,可以实现任意客户控制的管道   如果现有操作不是,则遍历   足以完成任务。

另一方面,在关闭Stream时,文档声明: -

  

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

要匹配的AutoCloseable州 -

  

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

这是BaseStream扩展它的方式,而close()对使用Files.lines(...)等资源的流的影响远远大于solution here

  

然而,当使用支持两者的Stream等设施时   基于I / O和非I / O的表单,try-with-resources块都在   使用非基于I / O的表单时一般不必要。

答案 3 :(得分:0)

方法stream.spliterator()不会关闭Stream,就像没有其他终端操作一样。您可能会或可能不会知道,是否需要关闭Stream,这仍然是一个很大的争论。我个人希望使用spliterator()iterator()以外的所有终端操作来出于各种原因隐式关闭Stream。这样做没有什么害处,因为大多数实现都只会执行什么

您可以在java.util.stream.AbstractPipeline中找到一种实现。调用stream.onClose(action)时,将调用调用stream.close()时添加的关闭动作。

我无法告诉您何时需要显式关闭Stream,因为当前策略是在返回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);
    }
}

现在的问题是,如果您希望有一个返回Spliterator的方法,则调用者没有close()上可以调用的Spliterator方法。