Як забезпечити порядок обробки в потоках java8?


148

Я хочу обробити списки всередині XMLоб’єкта java. Я повинен забезпечити обробку всіх елементів для того, щоб я їх отримав.

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

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

Відповіді:


338

Ви ставите неправильне запитання. Ви запитуєте про sequentialпорівняно, parallelтоді як ви хочете обробляти елементи в порядку , тому вам доведеться запитати про замовлення . Якщо у вас є упорядкований потік і виконуються операції, які гарантують підтримання порядку, не має значення, обробляється він паралельно або послідовно; реалізація буде підтримувати порядок.

Впорядкована властивість відрізняється від паралельної проти послідовної. Наприклад , якщо ви телефонуєте stream()на HashSetпотік буде неврегульованим при виклику stream()на через 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());

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


48
Так, гарна відповідь. Я знайшов одне, що термінологія, якою ми користуємось, принаймні англійською мовою, наприклад, "раніше", "після" тощо, є досить неоднозначною. Тут є два види впорядкування: 1) зустріч порядку (також відомий як просторовий порядок ) та 2) порядок обробки (також відомий як тимчасовий порядок ). Зважаючи на це розрізнення, може бути корисним використовувати такі слова, як "ліворуч" або "праворуч" при обговоренні порядку зустрічей і "раніше" або "пізніше" при обговоренні порядку обробки.
Стюарт Маркс

Я розумію List<>, збережу порядок, але чи буде Collection<>?
Джош К.

5
@JoshC. це залежить від фактичного типу колекції. Sets, як правило, ні, якщо це SortedSetабо LinkedHashSet. Перегляди колекцій Map( keySet(), entrySet()і values()) успадковують Mapполітику Росії, тобто впорядковуються, коли карта є SortedMapабо LinkedHashMap. Поведінка визначається характеристиками, про які повідомляє спліттератор колекції . defaultРеалізація Collectionне повідомляє ORDEREDхарактеристики, так що це невпорядковане, якщо не скасовано.
Холгер

@Holger У мене виникло запитання, яке може бути дещо пов'язане з невеликим розділом вашої відповіді.
Наман

1
Варто зауважити, що це forEachOrderedвідрізняється лише forEachпри використанні паралельних потоків, але хороша практика використовувати його все одно під час замовлення питань у випадку, якщо спосіб пропарювання колись зміниться ...
Стів Чемберс

0

Коротко:

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

Детальніше:

Послідовний проти Паралельний 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

Потоки можуть мати або не мати певного порядку зустрічі. Чи має потік порядок зустрічей чи ні, залежить від джерела та проміжних операцій. Окремі джерела потоку (такі як List або масиви) впорядковані, а інші (наприклад, HashSet). Деякі проміжні операції, такі як sorted (), можуть накладати замовлення на зустріч в іншому порядку, не упорядкованому, а інші можуть робити упорядкований потік не упорядкованим, наприклад BaseStream.unordered (). Крім того, деякі операції з терміналом можуть ігнорувати порядок зустрічей, наприклад forEach ().

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

Для послідовних потоків наявність або відсутність порядку зустрічей не впливає на продуктивність, лише на детермінізм. Якщо потік замовлений, повторне виконання однакових потокових трубопроводів на ідентичному джерелі дасть ідентичний результат; якщо його не замовлено, повторне виконання може призвести до різних результатів.

Для паралельних потоків послаблення обмежень для замовлення іноді може забезпечити більш ефективне виконання. Деякі сукупні операції, такі як фільтрація дублікатів (окремо ()) або групування скорочень (Collectors.groupingBy ()), можуть бути реалізовані ефективніше, якщо впорядкування елементів не має значення. Аналогічно, операції, які по суті пов'язані з порядком зустрічі, наприклад, limit (), можуть вимагати буферизації для забезпечення належного впорядкування, що підриває перевагу паралелізму. У випадках, коли потік має порядок зустрічі, але користувач не особливо дбає про цей порядок зустрічей, явно дезадація потоку не упорядкованим () може покращити паралельну продуктивність для деяких операцій стану або терміналу. Однак більшість потокових трубопроводів, наприклад, "сума ваги блоків", наведена вище,

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.