Пример невмешательства в 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()
.
Вот и все.