wake-up-neo.com

Beispiel für Nichteinmischung in Java 8

Nach dieser Frage können wir die Quelle modifizieren und es heißt nicht Interferenz:

sie können die Stream-Elemente selbst modifizieren und sollten nicht als "Interferenz" bezeichnet werden.

Nach dieser Frage , dem Code

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

wird ConcurrentModificationException werfen.

Aber mein Code,

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);

wirft keine ConcurrentModificationException, obwohl die Quelle tatsächlich geändert wird.

Und dieser Code,

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);

wirft

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)

Ich verstehe also nicht genau, welche Art von Änderungen an der Quelle erlaubt sind und welche nicht. Es wäre sehr hilfreich, ein Beispiel zu sehen, das stört und einen zustandsbehafteten und Nebeneffekt erzeugenden Strom hat, mit dem richtigen Hinweis darauf, was welcher ist.

12
amarnath harish

Wenn Sie dies tun:

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

Sie haben die Liste selbst nicht geändert, aber ein Element in dieser Liste; es wird also nicht wie erwartet ein ConcurrentModificationException ausgelöst.

Im letzten Codefragment verwenden Sie Array.asList.
Sie müssen wissen, dass Array.asList Über einem Array (einer bestimmten inneren ArrayList Klasse) ein bloßes Nur-Lese-Wrapper zurückgibt, das erklärt, warum add wird nicht unterstützt.

In der Tat überschreibt diese innere Klasse nicht AbstractList # add method by design; verursachend UnsupportedOperationException; und immer noch nicht wie erwartet ConcurrentModificationException.

Hier ist ein Beispiel ähnlich Ihrem letzten Snippet, das eine ConcurrentModificationException auslöst:

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);
} 

Beachten Sie, dass ich die Arrays.List - Rückgabe mit einem "wahren" ArrayList einhülle und das Schreiben zulasse, da dieses die add - Methode implementiert;)

12
Mik378

Ihr erstes Beispiel ändert die vorhandenen Elemente von Stream, fügt jedoch keine Elemente zur Quelle hinzu oder entfernt sie nicht. Deshalb ist es keine Störung.

In Ihrem zweiten Beispiel wird versucht, Störungen zu verursachen, indem Sie der Quelle während der Stream-Pipeline ein Element hinzufügen. Sie erhalten jedoch UnsupportedOperationException anstelle von ConcurrentModificationException, da Sie versuchen, Elemente zu einer List mit fester Größe hinzuzufügen (die von Arrays.asList zurückgegeben wird).

Ändern Sie Ihr zweites Beispiel in:

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);

und Sie sollten ConcurrentModificationException bekommen.

5
Eran

Dies wird als strukturelle on nicht-strukturelle Änderung der Quelle der Stream bezeichnet. Zum Beispiel sagt ArrayList doc:

lediglich den Wert eines Elements zu setzen, ist keine strukturelle Änderung ...

In Ihrem Beispiel bedeutet dies, dass durch das Ändern von Employee per se das List selbst nicht geändert wird (kein Element entfernt oder hinzugefügt wird). Das Ändern von List selbst schlägt jedoch mit einem ConcurrentModificationException fehl:

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

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

Es gibt jedoch Quellen, bei denen Interferenzen mehr als in Ordnung sind, die als schwach konsistenter Durchlauf bezeichnet werden, wie 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()));

Dies wird nicht mit ConcurrentModificationException fehlschlagen.

4
Eugene

Sie können die Größe der Liste, an der Sie gerade arbeiten, nicht ändern.

Im ersten Beispiel ändern Sie einen Wert des Employee-Objekts in Ihrer Liste.

In der Sekunde fügen Sie einen Eintrag in Ihre Liste ein.

Das ist der Unterschied!

2
Leviand

Ihr Code ändert das Stream-Element und nicht die Liste selbst:

s->{s.setName("newname") ändert einen Feldnamen im Element des Streams, der Stream und dessen Quelle werden dadurch nicht beeinträchtigt.

Das Java.lang.UnsupportedOperationException ist auch nicht verwandt, es liegt daran, dass Sie versuchen, add in einer Liste fester Größe (die das Ergebnis von Arrays.asList) ist, aufzurufen ruft add, remove usw. auf. Das Ergebnis von Arrays.asList ruft die Ausnahme für die nicht unterstützte Operation auf, da die Größe der Erfassung fest ist.

2
ernest_k

Ihr erstes Beispiel ändert nicht die Anfangsliste.

Ein Stream ist nur ein view in der Liste, daher ist die Liste initial überhaupt nicht von Ihrem Stream-Code betroffen.

Während das zweite Beispiel list.add() explizit verwendet. 

Das ist alles, was dazu gehört.

2
GhostCat