Розглянемо, 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 колекції, яка не блокує і спирається на вже встановлені правила неурядового -втручання. У випадку, якщо резервна копія потоку колекції буде змінена під час ітерації, це ConcurrentModificationExceptionможе бути кинуто або може виникнути непослідовий результат.
Четвертий підхід (паралельний Stream.forEach) -
Як вже було сказано, жодна гарантія дотримання порядку зустрічей, як очікувалося, у разі паралельних потоків. Можливо, що дії виконуються в різних нитках для різних елементів, що ніколи не може бути з цим forEachOrdered.
П'ятий підхід (паралельний Stream.forEachOrdered) -forEachOrdered буде обробляти елементи в порядку , зазначеному джерелі , незалежно від того , чи є послідовний або паралельний потік. Тому немає сенсу використовувати це при паралельних потоках.
List? Покажіть нам, як ви це заявили та підтвердили.