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

148

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

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

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

membersound
источник

Ответы:

339

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

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

Смотрите также раздел «Заказ» в java.util.streamдокументации пакета .

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

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

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

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

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

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

Holger
источник
48
Да, хороший ответ Одна вещь, которую я обнаружил, заключается в том, что используемая нами терминология, по крайней мере на английском языке, такая как «до», «после» и т. Д., Весьма неоднозначна. Здесь есть два вида упорядочения: 1) порядок встречи (также известный как пространственный порядок ) и 2) порядок обработки (также известный как временной порядок ). Учитывая это различие, может быть полезно использовать такие слова, как «слева от» или «справа от» при обсуждении порядка встречи и «раньше, чем» или «позже» при обсуждении порядка обработки.
Стюарт Маркс
Я понимаю List<>, сохранит порядок, но будет Collection<>ли?
Джош К.
5
@JoshC. это зависит от фактического типа коллекции. Setобычно нет, если только это SortedSetили LinkedHashSet. Взгляды Взимание Map( keySet(), entrySet()и values()) Наследовать Mapполитики «s, т.е. упорядочены , когда карта является SortedMapили LinkedHashMap. Поведение определяется характеристиками, представленными сплитератором коллекции . defaultРеализация Collectionне сообщает ORDEREDхарактеристики, так что это неупорядоченное, если не отменено.
Хольгер
@ Хольгер У меня был вопрос, который может быть связан с небольшим разделом вашего ответа.
Наман
1
Стоит отметить, что forEachOrderedотличается только forEachпри использовании параллельных потоков - но хорошая практика использовать его в любом случае при заказе имеет значение в случае, если метод обработки паром когда-либо изменится ...
Стив Чамберс
0

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

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

Больше деталей:

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

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

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

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

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

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

Saikat
источник