Рассмотрим, 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быть? Покажите нам, как вы объявили и создали его экземпляр.