Рассмотрим, List<String> stringList
что может быть напечатано разными способами с использованием конструкций Java 8 :
stringList.forEach(System.out::println); // 1) Iterable.forEach
stringList.stream().forEach(System.out::println); // 2) Stream.forEach (order maintained generally but doc does not guarantee)
stringList.stream().forEachOrdered(System.out::println); // 3) Stream.forEachOrdered (order maintained always)
stringList.parallelStream().forEach(System.out::println); // 4) Parallel version of Stream.forEach (order not maintained)
stringList.parallelStream().forEachOrdered(System.out::println); // 5) Parallel version ofStream.forEachOrdered (order maintained always)
Чем эти подходы отличаются друг от друга?
Первый подход ( Iterable.forEach
).
Обычно используется итератор коллекции, который рассчитан на отказоустойчивость, что означает, что он будет генерировать, ConcurrentModificationException
если базовая коллекция будет структурно изменена во время итерации. Как уже упоминалось в документе для ArrayList
:
Структурная модификация - это любая операция, которая добавляет или удаляет один или несколько элементов или явно изменяет размер резервного массива; просто установка значения элемента не является структурной модификацией.
Таким образом, это означает, что для ArrayList.forEach
установки значения допускается без каких-либо проблем. И в случае одновременного сбора, например, ConcurrentLinkedQueue
итератор будет слабосогласованным, что означает, что переданные действия forEach
могут вносить даже структурные изменения без ConcurrentModificationException
исключения. Но здесь изменения могут или не могут быть видны в этой итерации.
Второй подход ( Stream.forEach
) -
порядок не определен. Хотя это может не произойти для последовательных потоков, но спецификация не гарантирует этого. Также действие должно быть не вмешивающимся в природу. Как уже упоминалось в документе :
Поведение этой операции явно недетерминировано. Для параллельных потоковых конвейеров эта операция не гарантирует соблюдение порядка встречи потока, так как это принесет в жертву преимущество параллелизма.
Третий подход ( Stream.forEachOrdered
) -
действие будет выполняться в порядке встречи потока. Так что всякий раз, когда порядок вещей имеет значение, forEachOrdered
не задумываясь. Как уже упоминалось в документе :
Выполняет действие для каждого элемента этого потока в порядке встречи потока, если поток имеет определенный порядок встречи.
В то время как итерация по синхронному коллекции с первым подходом бы заблокировать коллекцию раз и проведут его через все вызовы к методу действия, но в случае потоков , которые они используют spliterator коллекции, которая не блокирует и опирается на уже установленные правилах неправительственного -interference. В случае, если поддержка итерации потока изменяется во время итерации, ConcurrentModificationException
может быть выброшено или может возникнуть противоречивый результат.
Четвертый подход (параллельный Stream.forEach
) -
как уже упоминалось, нет гарантии соблюдения порядка встречи, как ожидается в случае параллельных потоков. Возможно, что действие выполняется в разных потоках для разных элементов, что никогда не может иметь место forEachOrdered
.
Пятый подход (параллельный Stream.forEachOrdered
) -forEachOrdered
будет обрабатывать элементы в порядке , указанном источнике , независимо от того , является ли последовательный или параллельный поток. Поэтому нет смысла использовать это с параллельными потоками.
List
быть? Покажите нам, как вы объявили и создали его экземпляр.