Вопрос-ответ

How to ensure order of processing in java8 streams?

Как обеспечить порядок обработки в потоках java8?

Я хочу обрабатывать списки внутри XML объекта Java. Я должен обеспечить обработку всех элементов в порядке, в котором я их получил.

Поэтому я должен вызывать sequential для каждогоstream, который я использую? list.stream().sequential().filter().forEach()

Или достаточно просто использовать поток, пока я не использую параллелизм? list.stream().filter().forEach()

Переведено автоматически
Ответ 1

Вы задаете неправильный вопрос. Вы спрашиваете о sequential vs. parallel тогда как вы хотите обрабатывать элементы по порядку, поэтому вы должны спросить о заказе. Если у вас есть упорядоченный поток и вы выполняете операции, гарантирующие поддержание порядка, не имеет значения, обрабатывается ли поток параллельно или последовательно; реализация сохранит порядок.

Свойство ordered отличается от свойства parallel vs. sequential . Например. если вы вызываете stream() для a HashSet, поток будет неупорядоченным, в то время как вызов stream() для a List возвращает упорядоченный поток. Обратите внимание, что вы можете вызвать unordered(), чтобы освободить контракт упорядочивания и потенциально повысить производительность. Если в потоке нет упорядочения, восстановить порядок невозможно. (Единственный способ превратить неупорядоченный поток в упорядоченный - это вызвать sorted, однако результирующий порядок не обязательно является исходным порядком).

Смотрите также раздел “Порядок” в java.util.stream документации по пакету.

Чтобы обеспечить поддержание порядка на протяжении всей потоковой операции, вы должны изучить документацию источника потока, все промежуточные операции и работу терминала на предмет того, поддерживают ли они порядок или нет (или, в первую очередь, имеет ли источник порядок).

Это может быть очень тонким, например, Stream.iterate(T,UnaryOperator) создает упорядоченный поток, в то время как Stream.generate(Supplier) создает неупорядоченный поток. Обратите внимание, что вы также допустили распространенную ошибку в своем вопросе, поскольку forEach не поддерживает порядок. Вы должны использовать forEachOrdered, если хотите обрабатывать элементы потока в гарантированном порядке.

Итак, если ваш list в вашем вопросе действительно является java.util.List, его stream() метод вернет упорядоченный поток и filter не изменит порядок. Итак, если вы вызываете list.stream().filter() .forEachOrdered(), все элементы будут обрабатываться последовательно по порядку, тогда как для list.parallelStream().filter().forEachOrdered() элементы могут обрабатываться параллельно (например, фильтром), но действие терминала все равно будет вызываться по порядку (что, очевидно, уменьшит преимущество параллельного выполнения).

Если вы, например, используете операцию типа

List<…> result=inputList.parallelStream().map(…).filter(…).collect(Collectors.toList());

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

Ответ 2

В двух словах:

Порядок зависит от структуры исходных данных и операций промежуточного потока. Предполагая, что вы используете List обработка должна быть упорядоченной (поскольку filter последовательность здесь не изменится).

Подробнее:

Последовательный vs Параллельный vs неупорядоченный:

Javadocs

S sequential()
Returns an equivalent stream that is sequential. May return itself, either because the stream was already sequential, or because the underlying stream state was modified to be sequential.
This is an intermediate operation.
S parallel()
Returns an equivalent stream that is parallel. May return itself, either because the stream was already parallel, or because the underlying stream state was modified to be parallel.
This is an intermediate operation.
S unordered()
Returns an equivalent stream that is unordered. May return itself, either because the stream was already unordered, or because the underlying stream state was modified to be unordered.
This is an intermediate operation.

Упорядочение потоков:

Javadocs


Потоки могут иметь или не иметь определенный порядок встреч. Имеет ли поток порядок встреч или нет, зависит от источника и промежуточных операций. Некоторые источники потока (такие как списки или массивы) внутренне упорядочены, тогда как другие (такие как HashSet) - нет. Некоторые промежуточные операции, такие как sorted() , могут налагать порядок встреч на неупорядоченный поток, а другие могут сделать упорядоченный поток неупорядоченным, например BaseStream.unordered(). Кроме того, некоторые операции терминала могут игнорировать порядок встреч, например forEach().


Если поток упорядочен, большинство операций ограничено для работы с элементами в порядке их встречаемости; если источником потока является список, содержащий [1, 2, 3], то результат выполнения map(x -> x*2) должен быть [2, 4, 6]. Однако, если в источнике нет определенного порядка встреч, то любая перестановка значений [2, 4, 6] будет допустимым результатом.


Для последовательных потоков наличие или отсутствие порядка встреч не влияет на производительность, только на детерминизм. Если поток упорядочен, повторное выполнение идентичных потоковых конвейеров в идентичном источнике приведет к идентичному результату; если он не упорядочен, повторное выполнение может привести к другим результатам.


Для параллельных потоков ослабление ограничения порядка иногда может обеспечить более эффективное выполнение. Определенные операции агрегирования, такие как фильтрация дубликатов (distinct()) или групповые сокращения (Collectors.groupingBy()), могут быть реализованы более эффективно, если порядок элементов не имеет значения. Аналогично, операции, которые внутренне привязаны к порядку встреч, такие как limit(), могут потребовать буферизации для обеспечения надлежащего порядка, что сводит на нет преимущества параллелизма. В случаях, когда поток имеет порядок встреч, но пользователь не особенно заботится об этом порядке встреч, явное изменение порядка потока с помощью unordered() может улучшить параллельную производительность для некоторых операций с отслеживанием состояния или терминала. Однако большинство потоковых конвейеров, таких как приведенный выше пример "суммы весов блоков", по-прежнему эффективно распараллеливаются даже при ограничениях упорядоченности.


2023-09-25 01:31 java java-8 java-stream