Закрывает ли 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(), он, похоже, не касается операций 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?

Причиной этого вопроса является глубокая ясность, когда a stream требуется явно закрывать, а в тех случаях, когда мне не нужно явно закрывать его, когда будут выполняться определенные действия onClose()?

Ответ 1

Операции с терминалом никогда не закрывают поток. Закрытие должно выполняться вручную. Единственное место, где происходит автоматическое закрытие, находится внутри операции flatMap, где ручное закрытие подпотоков, обычно создаваемых "на лету", будет где-то между жестким и невозможным.

Это также относится к методу Stream.spliterator(). В ваших примерах это не имеет значения, потому что потоки, созданные с помощью Stream.of(…), не нужно закрывать и не имеют операции onClose, зарегистрированной по умолчанию.

Вам нужно проконсультироваться с документацией методов factory, чтобы узнать, когда поток необходимо закрыть, например. например Files#lines(Path, Charset).

См. также Собирает ли транзакция по потоку закрыть поток и основные ресурсы? или Является ли Java 8 Stream.iterator() закрыть поток, когда это будет сделано?

Ответ 2

Вызов метода spliterator() возвращает Spliterator для элементов этого потока и его терминальной операции.

Чтобы ответить на ваш вопрос: Нет, метод Spliterator или ни одна из других операций терминала также не закрывает поток.

Это подтверждается документацией терминальные операции как -

После выполнения операции терминала конвейер потока считаются потребляемыми и больше не могут использоваться... Почти во всех случаях терминальные операции нетерпеливый, завершая их обход источника данных и переработку трубопровода перед возвратом. Только терминал операции iterator() и spliterator() не являются; они предоставляются как "люк-побег" для включения произвольного управляемого клиентом конвейера обход в случае, если существующие операции не достаточный для выполнения задачи.

За закрытием Stream с другой стороны документы указывают, что: -

Большинство экземпляров потока фактически не нужно закрывать после использования, поскольку они подкрепляются коллекциями, массивами или генерирующими функциями, которые не требуют специального управления ресурсами. Как правило, только потоки, чьи source является каналом ввода-вывода, например, возвращаемым Files.lines(Path), потребует закрытия.


AutoCloseable утверждает, что соответствует ему -

Возможно, и фактически обычное, для базового класса для реализации AutoCloseable, хотя не все его подклассы или экземпляры будут иметь освобожденные ресурсы.

как это делает BaseStream, а close() не влияет гораздо больше, чем потоки поверх тех, которые используют ресурсы например Files.lines(...).

Однако при использовании таких средств, как Stream, которые поддерживают как Формы на основе ввода-вывода и не-ввода-вывода, блоки try-with-resources находятся в вообще не нужно при использовании форм, не содержащих I/O.

Ответ 3

Ничего не изменилось в отношении закрытия Stream в Java 9. Вам все равно нужно сделать это вручную, если базовый ресурс должен быть освобожден. Вы не должны полагаться на сборщика мусора, чтобы сделать это. docs все еще говорят:

Потоки имеют метод BaseStream.close() и реализуют AutoCloseable. Работа над потоком после его закрытия будет бросать IllegalStateException. Большинство экземпляров потока фактически не нужно закрывать после использования, поскольку они поддерживаются коллекциями, массивами или генерирующими функциями, которые не требуют специального управления ресурсами. Как правило, только потоки, источник которых является каналом ввода-вывода, например те, которые возвращаются Files.lines(Path), требуют закрытия. Если поток требует закрытия, он должен быть открыт как ресурс в операторе try-with-resources или аналогичной структуре управления, чтобы обеспечить его немедленное закрытие после завершения его операций.

Ответ 4

Метод stream.spliterator() не закрывает Stream, как никакая другая терминальная операция. Вы можете или не можете знать, если вам нужно закрыть Stream, это все еще большая дискуссия. Я лично хотел бы, чтобы все терминальные операции, кроме spliterator() и iterator() неявно закрывали Stream по разным причинам. В этом нет никакого вреда, так как большинство реализаций просто ничего не делают.

Вы можете найти одну реализацию в java.util.stream.AbstractPipeline. Действие close, которое было добавлено при вызове stream.onClose(action) будет stream.close() в момент stream.close().

Я не могу сказать вам, когда необходимо явно закрыть Stream, поскольку текущая политика заключается в том, чтобы документировать его с помощью метода, который возвращает Stream, например, Files.list(Path):

Возвращенный поток содержит ссылку на открытый каталог. Каталог закрывается при закрытии потока....

Этот метод должен использоваться в операторе try-with-resources или аналогичной структуре управления, чтобы гарантировать, что открытый каталог потока будет закрыт сразу после завершения операций потока.

Когда вы вызываете stream.spliterator(), возвращаемый Spliterator может быть ленивым (см. Также AbstractPipeline.lazySpliterator(..)), поэтому все еще должен работать с элементами исходного Stream. Если вы закроете Stream перед тем, как Spliterator через элементы 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.