2015-03-23 10 views
7

Ermöglicht bei diesem Beispiel einen Blick:Warum iterator.forEachRemaining das Element im Consumer Lambda nicht entfernt?

public class ListIteratorTest { 
    public static void main(String[] args) { 
     List<String> list = new ArrayList<>(); 
     list.add("element1"); 
     list.add("element2"); 
     list.add("element3"); 
     list.add("element4"); 

     ListIterator<String> iterator = list.listIterator(); 
    } 
} 

Und jetzt funktioniert das gut:

// prints elements out, and then appropriately removes one after another 
    while (iterator.hasNext()){ 
     System.out.println(iterator.next()); 
     iterator.remove(); 
    } 

während dies eine Illegal wirft:

 // throws IllegalStateException, why? 
     iterator.forEachRemaining(n -> { 
      System.out.println(n); 
      iterator.remove(); 
     }); 

Meine Frage ist kurz: warum ?

+0

Lambda-Ausdrücke (und natürlich auch Methodenverweise) haben den großen Vorteil, viel lesbareren oder saubereren Code zu erzeugen. Ich neige dazu, sie zu verwenden, wo immer ich einen wirklichen Vorteil - in Bezug auf die Lesbarkeit - von ihnen bekomme. Ihr Lambda-Beispiel ist nicht lesbarer oder weniger lesbar als das Beispiel, das keinen Lambda-Ausdruck verwendet. In der Tat hat es die gleiche Anzahl von Codezeilen. Also warum fühlst du, dass du hier Lambdas benutzen musst (oder solltest)? In der Tat ... Sie haben es entdeckt ... sie machen Ihren Code etwas kompliziert, da Sie den Iterator nicht innerhalb des Lambda verwenden sollten. – Seelenvirtuose

+0

@Seelenvirtuose Ich bin immer noch mit den Lernfähigkeiten von Java8 beschäftigt, also versuche ich, lambdas usw. zu verwenden, wo immer ich kann. Ich dachte nur, wenn es möglich ist und wollte es überprüfen - es hatte unerwartete Ergebnisse gegeben und ich wusste nicht, warum so gefragt hier; Aber danke für die Erklärung dessen, wofür Lambas eigentlich gemacht sind - der Einfachheit halber wo möglich – azalut

Antwort

12

Aktualisiert dank @studro. Siehe seinen Kommentar unten.

Die API documentation Zustände:

Das Verhalten eines Iterators ist nicht spezifiziert, wenn die zugrunde liegende Auflistung geändert wird, während der Iteration läuft in irgendeiner Weise anders als durch das Aufrufen dieser Methode ist.

Es scheint, dass der Teil "nicht spezifiziertes Verhalten" auch während dieser internen Iteration gilt.

Zugegeben, heißt es in der Dokumentation für forEachRemaining, dass das Verhalten zu

while (hasNext()) 
    action.accept(next()); 

entspricht und wenn action::accept tatsächlich Anruf tat iterator.remove() das obige Snippet sollte keine Ausnahme auslösen (wenn remove eine unterstützte Operation). Dies könnte ein Dokumentationsfehler sein.

+0

also, ist es möglich, die Liste mit lambdas zu durchlaufen (zum Beispiel iterator.forEachRemaining()) und das Element nach der Bearbeitung zu löschen? – azalut

+3

Sicher. Sie können die zu löschenden Elemente in einer separaten Liste speichern, 'toDelete', und nach dem forEachRemaining können Sie' list.removeAll (toDelete) 'verwenden. Das ist jedoch nicht der idiomatische Weg, um es in Java 8 zu lösen. Sie möchten wahrscheinlich 'stream.filter' verwenden. – aioobe

+4

... oder einfach 'list.removeIf (...)', wenn Sie die Liste in idiomatischer Java-8-Weise ändern möchten. –

Verwandte Themen