简体   繁体   中英

Does the stream.spliterator() close the stream?

Does the stream.spliterator() implicitly closes the stream , or there is a need to explicitly close it afterwards?

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?

At first glance, it seems that the .spliterator() method closes the stream , but without calling stream.close() . At least if I close it straight away after the .spliterator() method is invoked, it seems not affection the spliterator operations.

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

This question can be extended to other stream methods, for example, the .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?

The reason for that question is to have deep clarity when a stream needs explicitly to be closed, and in the cases where I don't need to explicitly close it, when the onClose() defined actions will take place?

Terminal operations do never close the stream. Closing has to be done manually. The only place where automatic closing happens, is within the flatMap operation, where manual closing of the substreams usually created on-the-fly would be somewhere between hard and impossible.

This also applies to the Stream.spliterator() method. In your examples, it makes no difference, because the streams created via Stream.of(…) do not need to be closed and have no onClose operation registered by default.

You have to consult the documentation of the factory methods to find out when a stream need to be closed, eg like with Files#lines(Path, Charset) .

See also Does collect operation on Stream close the stream and underlying resources? or Does Java 8 Stream.iterator() auto-close the stream when it's done?

Nothing changed concerning the closing of Stream s in Java 9. You still need to manually do it if the underlying resource should be freed. You should never rely on the garbage collector to do it. The docs still say:

Streams have a BaseStream.close() method and implement AutoCloseable . Operating on a stream after it has been closed will throw IllegalStateException . Most stream instances do not actually need to be closed after use, as they are backed by collections, arrays, or generating functions, which require no special resource management. Generally, only streams whose source is an IO channel, such as those returned by Files.lines(Path) , will require closing. If a stream does require closing, it must be opened as a resource within a try-with-resources statement or similar control structure to ensure that it is closed promptly after its operations have completed.

The call to spliterator() method returns a Spliterator for the elements of this stream and its a terminal operation.

To answer your question - No, the spliterator method or for that sake none of the other terminal operations also does not close the stream.

This stands documented for terminal operations as -

After the terminal operation is performed, the stream pipeline is considered consumed, and can no longer be used.... In almost all cases, terminal operations are eager , completing their traversal of the data source and processing of the pipeline before returning. Only the terminal operations iterator() and spliterator( ) are not ; these are provided as an "escape hatch" to enable arbitrary client-controlled pipeline traversals in the event that the existing operations are not sufficient to the task.

Over closing the Stream on another hand the docs state that:-

Most stream instances do not actually need to be closed after use, as they are backed by collections, arrays, or generating functions, which require no special resource management. Generally, only streams whose source is an IO channel, such as those returned by Files.lines(Path) , will require closing.


The AutoCloseable states to match to it-

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.

which is how the BaseStream extends it and the close() doesn't impact far more than the streams over the ones using the resources such as Files.lines(...) .

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.

The method stream.spliterator() does not close the Stream , like no other terminal operation. You may or may not know, if you need to close the Stream , that's still a big debate. I personally would like to have all terminal operations except spliterator() and iterator() to implicitly close the Stream for various reasons. There is no harm in doing so, since most implementations would simply do nothing .

You can find one implementation in the java.util.stream.AbstractPipeline . The close action that has been added when calling stream.onClose(action) will be called in the moment stream.close() is called.

I cannot tell you when a Stream explicitly needs to be closed, since the current policy is to document it at the method that returns the Stream , like Files.list(Path) :

The returned stream contains a reference to an open directory. The directory is closed by closing the stream. ...

This method must be used within a try-with-resources statement or similar control structure to ensure that the stream's open directory is closed promptly after the stream's operations have completed.

When you call stream.spliterator() , the returned Spliterator may be lazy (see also AbstractPipeline.lazySpliterator(..) ), thus still needs to operate on the elements of the original Stream . If you close the Stream before traversing the elements of the Spliterator , you might get an exception or the Spliterator simply does not operate anymore.

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);
    }
}

The problem is now, if you want to have a method that returns a Spliterator , the caller has no close() method that can be called on the Spliterator .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM