Перейти к основному содержимому

«Поток уже обработан или закрыт» Исключение в Java

· 2 мин. чтения

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 .