web-dev-qa-db-ger.com

wie entferne ich ein Objekt aus dem Stream in der foreach-Methode?

ich muss Arrays: arrA und arrB. arrA und arrB sind Listen von Objekten verschiedener Typen und die Funktion add konvertiert Objekte A in Objekte B. Ich möchte jedes Objekt von arrA zu arrB hinzufügen und dieses Objekt aus arrA entfernen. Ich versuche dies per Stream:

arrA.stream().foreach(c -> {arrB.add(c); arrA.remove(c);});

wenn ich das ausführen, passiert Folgendes:

  1. nicht alle objekte werden von arrA an arrB übergeben.
  2. nach einigen Iterationen wird eine Nullzeigerausnahme ausgelöst.

i gues, weil die Länge des Arrays nach jedem Aufruf von remove() verringert wird und der Iterationszähler erhöht wird (nur Objekte unter ungeraden Indizes werden an arrB übergeben)

Jetzt könnte ich dies lösen, indem ich das Array in einem Stream-Aufruf kopiere und dann Objekte im zweiten Stream-Aufruf entferne, aber dies scheint mir nicht korrekt zu sein.

Was wäre die richtige Lösung für dieses Problem?

EDIT . Zusätzliche Informationen: In realer Implementierung diese Liste, wenn zuvor gefiltert

arrA.stream().filter(some condition).foreach(c -> {arrB.add(c); arrA.remove(c);});

und es wird einige Male aufgerufen, Elemente hinzuzufügen, die verschiedene Bedingungen für verschiedene Listen (arrC, arrD usw.) erfüllen, aber jedes Objekt kann nur in einer Liste enthalten sein

7
Akka Jaworek

Streams sind so konzipiert, dass sie funktionaler genutzt werden können und vorzugsweise Ihre Sammlungen als unveränderlich behandeln.

Der Nicht-Stream-Weg wäre:

arrB.addAll(arrA);
arrA.clear();

Möglicherweise verwenden Sie jedoch Streams, sodass Sie die Eingabe filtern können.

arrB.addAll(arrA.stream().filter(x -> whatever).toList())

dann von arrA entfernen (Dank an @Holgar für den Kommentar).

arrA.removeIf(x -> whatever)

Wenn Ihr Prädikat teuer ist, können Sie partitionieren:

Map<Boolean, XXX> lists = arrA.stream()
  .collect(Collectors.partitioningBy(x -> whatever));
arrA = lists.get(false);
arrB = lists.get(true);

oder machen Sie eine Liste der Änderungen:

List<XXX> toMove = arrA.stream().filter(x->whatever).toList();
arrA.removeAll(toMove);
arrB.addAll(toMove);
15
davidsheldon

Wie von den anderen bereits erwähnt, ist dies mit foreach nicht möglich, da mit der for (A a: arrA)-Schleife keine Elemente entfernt werden können.

Meiner Meinung nach ist die sauberste Lösung die Verwendung einer Ebene für while mit Iteratoren. Mit Iteratoren können Sie Elemente während der Iteration entfernen (sofern die Auflistung dies unterstützt).

Iterator<A> it = arrA.iterator()
while (it.hasNext()) {
    A a = it.next();
    if (!check(a))
        continue;
    arrB.add(a);
    it.remove();
}

Dies erspart Ihnen auch das Kopieren/Klonen von arrA.

4
Martin Nyolt

Ich glaube nicht, dass Sie ihn von arrA entfernen können, während Sie darüber iterieren.

Sie können dies umgehen, indem Sie es in eine neue ArrayList <> () packen.

neue ArrayList <> (arrA) .stream (). foreach (c -> {arrB.add (c); arrA.remove (c);});

1
Mike Samaras

ich denke, das liegt daran, dass die Länge des Arrays nach jedem Aufruf von remove () abnimmt und der Iterationszähler erhöht wird

Recht. Die for-each-Schleife ist wie eine normale for-Schleife, aber einfacher zu schreiben und zu lesen. Man kann es sich als syntaktischen Zucker vorstellen. Intern werden entweder Iterator- oder Array-Indizes verwendet. Die forEach-Methode von Streams ist eine etwas ausgefallenere Version davon, die parallele Ausführung und funktionalen Codierungsstil ermöglicht, hat jedoch ihre eigenen Nachteile .

Wie bei jeder indizierten Schleife wird beim Entfernen eines Elements während der Schleife die Schleife unterbrochen. Erwägen Sie drei Elemente mit den Indizes 0, 1 und 2. Wenn Sie das Element 0 in der ersten Iteration entfernen, werden die Listenelemente um eins nach oben verschoben, und bei der nächsten Iteration werden die Elemente 0 (zuvor 1) und 1 (zuvor 2) angezeigt . Ihre Schleifenvariable zeigt jetzt auf 1 und überspringt den tatsächlich nächsten Eintrag. Beim Index 2 erhält die Schleife, an der Sie gerade arbeiten, nur noch ein Element (Sie haben zwei entfernt), wodurch ein Fehler ausgegeben wird, da der Index außerhalb der Grenzen liegt.

Mögliche Lösungen:

  • Verwenden Sie die Methoden List zum Klonen und Löschen von Listen.
  • Machen Sie es mit zwei Schleifen, wenn Sie wirklich die Methoden für jedes einzelne Element aufrufen müssen.
1