Пример невмешательства в Java 8

В соответствии с этим вопросом мы можем изменить источник, и он не называется помехой:

вы можете сами изменить элементы потока, и его не следует называть "помехой".

Согласно этому вопросу, код

List<String> list = new ArrayList<>();
  list.add("test");
  list.forEach(x -> list.add(x));

будет вызывать ConcurrentModificationException.

Но мой код,

Employee[] arrayOfEmps = {
                new Employee(1, "Jeff Bezos"),
                new Employee(2, "Bill Gates"),
                new Employee(3, "hendry cavilg"),
                new Employee(4, "mark cuban"),
                new Employee(5, "zoe"),
                new Employee(6, "billl clinton"),
                new Employee(7, "ariana") ,
                new Employee(8, "cathre"),
                new Employee(9, "hostile"),
                new Employee(10, "verner"),
            };
        Employee el=new Employee(1, "Jeff Bezos");
        List<Employee> li=Arrays.asList(arrayOfEmps);
        li.stream().map(s->{s.setName("newname");return s;}).forEach(System.out::print);

не бросает ConcurrentModificationException, даже если он фактически меняет источник.

И этот код,

Employee[] arrayOfEmps = {
                new Employee(1, "Jeff Bezos"),
                new Employee(2, "Bill Gates"),
                new Employee(3, "hendry cavilg"),
                new Employee(4, "mark cuban"),
                new Employee(5, "zoe"),
                new Employee(6, "billl clinton"),
                new Employee(7, "ariana") ,
                new Employee(8, "cathre"),
                new Employee(9, "hostile"),
                new Employee(10, "verner"),
            };
        Employee el=new Employee(1, "Jeff Bezos");
        List<Employee> li=Arrays.asList(arrayOfEmps);
        li.stream().map(s->{s.setName("newname");li.add(s);return s;}).limit(10).forEach(System.out::print);

бросает

Exception in thread "main" java.lang.UnsupportedOperationException
    at java.util.AbstractList.add(Unknown Source)
    at java.util.AbstractList.add(Unknown Source)
    at java8.Streams.lambda$0(Streams.java:33)
    at java.util.stream.ReferencePipeline$3$1.accept(Unknown Source)
    at java.util.Spliterators$ArraySpliterator.forEachRemaining(Unknown Source)
    at java.util.stream.AbstractPipeline.copyInto(Unknown Source)
    at java.util.stream.AbstractPipeline.wrapAndCopyInto(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp.evaluateSequential(Unknown Source)
    at java.util.stream.ForEachOps$ForEachOp$OfRef.evaluateSequential(Unknown Source)
    at java.util.stream.AbstractPipeline.evaluate(Unknown Source)
    at java.util.stream.ReferencePipeline.forEach(Unknown Source)

Итак, я не совсем понимаю, какие типы модификаций разрешены для источника, а какие нет. Было бы очень полезно увидеть пример, который вмешивается и имеет поток, генерирующий состояние и побочный эффект, с правильным указанием того, что есть.

Ответ 1

Когда вы это сделаете:

li.stream().map(s->{s.setName("newname");return s;})

вы не изменили сам список, а элемент в этом списке; поэтому он не вызывает ConcurrentModificationException как вы ожидали.

В последнем фрагменте кода вы используете Array.asList.
Вы должны знать, что Array.asList возвращает простую оболочку только для чтения по массиву (конкретный внутренний класс ArrayList), объясняя, почему add не поддерживается.

Действительно, этот внутренний класс не переопределяет метод AbstractList # add по дизайну; вызывая UnsupportedOperationException; и все еще не ConcurrentModificationException как вы и ожидали.

Вот пример, похожий на ваш последний фрагмент, который вызывает бросок ConcurrentModificationException:

public static void main(String[] args) {
    Employee[] arrayOfEmps = {
      new Employee(1, "Jeff Bezos")
    };
    Employee el = new Employee(11, "Bill Gates");
    List<Employee> li = new ArrayList<>(Arrays.asList(arrayOfEmps)); // to avoid read-only restriction
    li.stream().peek(s -> li.add(el)).forEach(System.out::print);
} 

Обратите внимание, что я обертываю Arrays.List return с помощью "истинного" ArrayList, позволяя писать, потому что этот реализует метод add ;)

Ответ 2

Ваш первый пример изменяет существующие элементы Stream, но не добавляет и не удаляет элементы из источника. Поэтому это не помеха.

Второй пример пытается вмешаться, добавив элемент в источник во время конвейера Stream. Тем не менее, вы получите UnsupportedOperationException вместо ConcurrentModificationException, так как вы пытаетесь добавить элементы к размеру фиксированного List (который возвращается Arrays.asList).

Измените свой второй пример на:

List<Employee> li=new ArrayList<>(Arrays.asList(arrayOfEmps));
li.stream().map(s->{s.setName("newname");li.add(s);return s;}).limit(10).forEach(System.out::print);

и вы должны получить ConcurrentModificationException.

Ответ 3

Это называется структурным по неструктурным изменениям источника Stream. Например, документ ArrayList говорит:

просто установка значения элемента не является структурной модификацией...

Таким образом, в вашем примере это означает, что изменение Employee per-se не изменяет сам List (не удаляет или не добавляет элемент). Но изменение самого List приведет к ошибке с помощью ConcurrentModificationException:

List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);

list.stream().forEach(x -> list.remove(x));

Но есть источники, где помехи больше, чем ОК, называемые слабо согласованными обходами, такими как ConcurrentHashMap:

 ConcurrentHashMap<Integer, String> chm = new ConcurrentHashMap<>();
 chm.put(1, "one");
 chm.put(2, "two");
 chm.put(3, "three");

 chm.entrySet().stream()
         .forEach(x -> chm.remove(x.getKey()));

Это не сработает с ConcurrentModificationException.

Ответ 4

Вы не можете изменить размер списка, над которым работаете.

В первом примере вы меняете значение объекта Employee внутри вашего списка.

Во втором вы добавляете элемент в свой список.

Это разница!

Ответ 5

Ваш код изменяет элемент потока, а не сам список:

s->{s.setName("newname") изменяет имя поля в элементе потока, это не мешает потоку или его источнику.

java.lang.UnsupportedOperationException также не связано, что вызвано тем, что вы пытаетесь вызвать add в списке фиксированного размера (который является результатом Arrays.asList). Каждый раз, когда вы вызываете add, remove и т.д. Из результата Arrays.asList, возникает исключение неподдерживаемой операции, так как сбор фиксируется по размеру.

Ответ 6

Ваш первый пример не изменяет первоначальный список.

Поток - это просто представление в списке, поэтому исходный список не влияет на ваш код потока.

В то время как во втором примере явно используется list.add().

Вот и все.