Должен ли я использовать общее изменение изменяемой переменной в Java 8 Streams - PullRequest
0 голосов
/ 21 сентября 2018

Просто повторяем приведенный ниже список и добавляем в другой общий изменяемый список через потоки Java 8.

List<String> list1 = Arrays.asList("A1","A2","A3","A4","A5","A6","A7","A8","B1","B2","B3");
List<String> list2 = new ArrayList<>();

Consumer<String> c = t -> list2.add(t.startsWith("A") ? t : "EMPTY");

list1.stream().forEach(c);
list1.parallelStream().forEach(c);
list1.forEach(c);

В чем разница между тремя итерациями и тем, что нам нужно использовать.Есть какие-нибудь соображения?

Ответы [ 3 ]

0 голосов
/ 21 сентября 2018

С функциональной точки зрения, для простых случаев они почти одинаковы, но, вообще говоря, есть некоторые скрытые различия:

  1. Давайте начнем с цитирования из Javadoc forEach для повторяемых сценариев использования, в которых указано:

выполняет заданное действие для каждого элемента Iterable до тех пор, пока все элементы не будут обработаны или действие не вызовет исключение.

а также мы можем перебирать коллекцию и выполнять заданное действие для каждого элемента - просто передав класс, реализующий интерфейс Consumer

void forEach(Consumer<? super T> action)

https://docs.oracle.com/javase/8/docs/api/java/lang/Iterable.html#forEach-java.util.function.Consumer-


Порядок Stream.forEach является случайным, в то время как Iterable.forEach всегда выполняется в порядке итерации Iterable.
Если Iterable.forEach выполняет итерацию по синхронизированной коллекции , Iterable.forEach один раз берет блокировку коллекции и удерживает ее во всех вызовах метода действия.Вызов Stream.forEach использует сплитератор коллекции, который не блокирует
Действие, указанное в Stream.forEach, должно быть без помех , в то время как Iterable.forEach разрешено устанавливать значения в базовом ArrayList без проблем.
В Java итераторы, возвращаемые классами Collection, например, ArrayList, HashSet, Vector и т. Д., Быстро перестают работать.Это означает, что если вы попытаетесь добавить () или удалить () из базовой структуры данных во время итерации, вы получите ConcurrentModificationException.

https://docs.oracle.com/javase/8/docs/api/java/util/ArrayList.html#fail-fast


Подробнее:

0 голосов
/ 30 апреля 2019

Я лично считаю, что при работе с потоками вы должны писать свой код таким образом, чтобы при переключении на параллельные потоки он не нарушал ваш код (приводил к неверным результатам).Представьте, что в вашем коде вы выполняете чтение и запись в одной и той же общей памяти (list2) и распределяете процесс по нескольким потокам (используя параллельные потоки).Тогда вы обречены.Поэтому у вас есть несколько вариантов.

сделать вашу общую память (list2) потокобезопасной.например, используя AtomicReferences

List<String> list2 = new ArrayList<>();
AtomicReference<List<String>> listSafe = new AtomicReference<>();
listSafe.getAndUpdate(strings -> {strings.add("newvalue"); return strings;}); 

или вы можете использовать чисто функциональный подход (код без побочных эффектов), такой как решение @Eran.

0 голосов
/ 21 сентября 2018

Независимо от того, используете ли вы параллельный или последовательный Stream, вы не должны использовать forEach, когда ваша цель - сгенерировать List.Используйте map с collect:

List<String> list2 = 
    list2.stream()
         .map(item -> item.startsWith("A") ? item : "EMPTY")
         .collect(Collectors.toList());
...