Скажем, у меня есть этот список фруктов: -
List<String> f = Arrays.asList("Banana", "Apple", "Grape", "Orange", "Kiwi");
Мне нужно добавить серийный номер к каждому фрукту и распечатать его. Порядок фруктов или порядковый номер не имеет значения. Итак, это правильный вывод: -
4. Kiwi
3. Orange
1. Grape
2. Apple
5. Banana
Решение №1
AtomicInteger number = new AtomicInteger(0);
String result = f.parallelStream()
.map(i -> String.format("%d. %s", number.incrementAndGet(), i))
.collect(Collectors.joining("\n"));
Решение №2
String result = IntStream.rangeClosed(1, f.size())
.parallel()
.mapToObj(i -> String.format("%d. %s", i, f.get(i - 1)))
.collect(Collectors.joining("\n"));
Вопрос
Почему решение № 1 является плохой практикой? Во многих местах я видел, что решения на основе AtomicInteger
плохи (например, в этом ответе), особенно при параллельной потоковой обработке (по этой причине я использовал параллельные потоки выше, чтобы попытаться столкнуться с проблемами).
Я просмотрел эти вопросы/ответы: -
В каких случаях Stream операции должны иметь состояние?
Является ли использование AtomicInteger для индексации в Stream законным способом?
Java 8: предпочтительный способ подсчета итераций лямбды?
Они просто упоминают (если я что-то не пропустил) «могут произойти неожиданные результаты». Как, например? Может ли это произойти в этом примере? Если нет, можете ли вы привести пример, где это может произойти?
Что касается «нет никаких гарантий относительно порядка применения функции отображения», ну, такова природа параллельной обработки, так что я принимаю это, а также порядок не имеет значения в этом конкретном примере.
AtomicInteger
является потокобезопасным, так что это не должно быть проблемой при параллельной обработке.
Может ли кто-нибудь привести примеры, в каких случаях возникнут проблемы при использовании такого решения на основе состояния?