[Решено] Исключение «Поток уже был обработан или закрыт»

Java 8 Stream API используется для обработки наборов данных, таких как массивы, списки и карты. Если мы часто работаем с потоками, мы могли столкнуться со следующей ошибкой: java.lang.IllegalStateException: поток уже был обработан или закрыт.

Давайте рассмотрим основную причину проблемы и пути ее решения.

1. Причина

Java Streams обрабатывает элементы коллекции с помощью двух типов операций:

  • Промежуточные операции: помогают в объединении методов в цепочку и возвращают новый поток обработанных элементов, таких как фильтр, сортировка или отображение.
  • Агрегатные или терминальные операции: завершают поток, выполняя общие операции или сокращение над ними. После вызова терминальной операции поток считается закрытым. Например, max, min или собрать элементы в список или набор.

Мы можем использовать терминальную операцию в потоке только один раз. Вызов терминальной операции в уже закрытом потоке приводит к исключению IllegalStateException.

Давайте разберемся на примере. В следующем примере мы используем один и тот же поток дважды. Сначала для получения четных чисел, а затем для получения нечетных чисел.

Stream<Integer> numberStream = Stream.of(123, 234, 11, 57, 60, -4);List<Integer> evenNumbers = numberStream.filter(integer -> integer % 2 == 0).collect(Collectors.toList());List<Integer> oddNumbers = numberStream.filter(integer -> integer % 2 == 1).collect(Collectors.toList());

Вывод программы.

Even numbers:23460-4Exception in thread "main" java.lang.IllegalStateException: stream has already been operated upon or closedat java.base/java.util.stream.AbstractPipeline.<init>(AbstractPipeline.java:203)at java.base/java.util.stream.ReferencePipeline.<init>(ReferencePipeline.java:94)at java.base/java.util.stream.ReferencePipeline$StatelessOp.<init>(ReferencePipeline.java:696)at java.base/java.util.stream.ReferencePipeline$2.<init>(ReferencePipeline.java:165)at java.base/java.util.stream.ReferencePipeline.filter(ReferencePipeline.java:164)at org.example.Main.main(Main.java:18)Process finished with exit code 1

Мы видим, что первая операция фильтрации потока была выполнена успешно, но вторая операция вернула исключение. Это вызвано тем, что поток потребляется и закрывается первой операцией, точнее функцией collect().

Когда мы начали обрабатывать поток во второй раз, мы получили исключение с сообщением «поток уже был обработан или закрыт».

2. Решение

Одним из решений этой проблемы было бы создание нового потока каждый раз перед каждой обработкой потока, но существует более элегантный метод решения этой проблемы.

Интерфейс Supplier — это функциональный интерфейс, который можно назначить с помощью лямбда-функции. После инициализации поставщика мы можем использовать функцию get(). Supplier.get() каждый раз возвращает вновь созданный объект Stream, на котором мы можем безопасно выполнять потоковые операции.

Supplier<Stream<Integer>> streamSupplier =() -> Stream.of(123, 234, 11, 57, 60, -4);List<Integer> evenNumbers = streamSupplier.get().filter(integer -> integer % 2 == 0).collect(Collectors.toList());List<Integer> oddNumbers = streamSupplier.get().filter(integer -> integer % 2 == 1).collect(Collectors.toList());

После запуска приведенного выше кода у нас не возникнет никаких ошибок, и поток будет обработан оба раза успешно.

3. Заключение

В этом коротком руководстве мы узнали основную причину и решение исключения потока «IllegalStateException: поток уже был обработан или закрыт». Мы научились исправлять ошибку с помощью поставщика, а также создавать новый поток каждый раз.

Прокрутить вверх