1. Обзор
В этой краткой статье мы собираемся обсудить распространенное исключение
, с которым мы можем столкнуться при работе с классом Stream
в Java 8:
IllegalStateException: stream has already been operated upon or closed.
Мы обнаружим сценарии, когда возникает это исключение, и возможные способы его избежать, а также практические примеры.
2. Причина
В Java 8 каждый класс Stream
представляет одноразовую последовательность данных и поддерживает несколько операций ввода-вывода.
Над
потоком
следует работать (вызывая операцию промежуточного или конечного потока) только один раз. Реализация Stream можетвызвать исключение IllegalStateException
, если обнаружит, чтоStream
используется повторно.
Всякий раз, когда терминальная операция вызывается для объекта Stream
, экземпляр потребляется и закрывается.
Поэтому нам разрешено выполнять только одну операцию, которая использует Stream ,
в противном случае мы получим исключение, указывающее, что Stream
уже обработан или закрыт.
Давайте посмотрим, как это можно перевести на практический пример:
Stream<String> stringStream = Stream.of("A", "B", "C", "D");
Optional<String> result1 = stringStream.findAny();
System.out.println(result1.get());
Optional<String> result2 = stringStream.findFirst();
Как результат:
A
Exception in thread "main" java.lang.IllegalStateException:
stream has already been operated upon or closed
После вызова метода #findAny()
stringStream
закрывается, поэтому любая дальнейшая операция над потоком
вызовет исключение IllegalStateException
, что и произошло после вызова метода #findFirst()
.
3. Решение
Проще говоря, решение состоит в создании нового потока
каждый раз, когда он нам нужен.
Мы, конечно, можем сделать это вручную, но здесь функциональный интерфейс Поставщика
становится очень удобным:
Supplier<Stream<String>> streamSupplier
= () -> Stream.of("A", "B", "C", "D");
Optional<String> result1 = streamSupplier.get().findAny();
System.out.println(result1.get());
Optional<String> result2 = streamSupplier.get().findFirst();
System.out.println(result2.get());
Как результат:
A
A
Мы определили объект streamSupplier
с типом Stream<String>
, который является точно таким же типом, который возвращает метод #get()
. Поставщик основан
на лямбда-выражении, которое не принимает никаких входных данных и возвращает новый поток
.
Вызов функционального метода get()
для Supplier
возвращает только что созданный объект Stream
, над которым мы можем безопасно выполнить другую операцию Stream .
5. Вывод
В этом кратком руководстве мы увидели, как выполнять терминальные операции с потоком
несколько раз, избегая при этом знаменитого исключения IllegalStateException
, которое выдается, когда поток
уже закрыт или над ним выполняются операции.
Вы можете найти полный исходный код и все фрагменты кода для этой статьи на GitHub .