2015-03-23 7 views
7

Diamo un'occhiata a questo esempio:Perché iterator.forEachRemaining non rimuove l'elemento nel lambda Consumer?

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

E ora, questo funziona bene:

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

mentre questo genera un IllegalStateException:

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

La mia domanda è breve: perché ?

+0

Le espressioni lambda (e ovviamente anche i riferimenti ai metodi) hanno il grande vantaggio di creare codice molto più leggibile o pulito. Io tendo ad usarli ovunque io ottenga un vantaggio reale - in termini di leggibilità - da loro. Il tuo esempio lambda non è più leggibile o meno leggibile, quindi l'esempio che non usa un'espressione lambda. In effetti, ha lo stesso numero di linee di codice. Allora, perché senti, che devi (o dovresti) usare lambda qui? Infatti ... l'hai rilevato ... rendono il tuo codice un po 'complicato, perché non dovresti usare l'iteratore all'interno del lambda. – Seelenvirtuose

+0

@Seelenvirtuose Sono ancora in corso con le capacità di apprendimento di Java8, quindi cerco di utilizzare lambdas, ecc. Ovunque io possa. Ho solo pensato che fosse possibile e volevo controllarlo - aveva dato risultati inattesi e non sapevo perché lo chiedessi qui; ma grazie per la spiegazione di cosa sono realmente fatti i lambas - per semplicità ove possibile – azalut

risposta

12

Aggiornato grazie a @studro. Vedi il suo commento qui sotto.

I API documentation stati:

Il comportamento di un iteratore è specificato se la raccolta sottostante viene modificato mentre l'iterazione è in corso in alcun modo diverso richiamando questo metodo.

Sembra che la parte "comportamento non specificato" si applichi anche durante questa iterazione interna.

Certo, la documentazione per forEachRemaining afferma che il comportamento è equivalente a

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

e se action::accept fecero infatti chiamata iterator.remove() quanto sopra frammento non dovrebbe gettare alcuna eccezione (se remove è un'operazione supportata). Questo potrebbe essere un bug di documentazione.

+0

allora, è possibile iterare attraverso l'elenco usando lambdas (ad esempio iterator.forEachRemaining()) ed eliminare l'elemento dopo averlo eseguito? – azalut

+3

Sicuro. È possibile memorizzare gli elementi da eliminare in una lista separata, 'toDelete', e dopo che forEachRemaining è stato completato, è possibile usare' list.removeAll (toDelete) '. Questo non è tuttavia il modo idiomatico per risolverlo in Java 8. Probabilmente si vorrà usare 'stream.filter'. – aioobe

+4

... o semplicemente 'list.removeIf (...)' se si desidera modificare l'elenco in un modo java 8 idiomatico. –

Problemi correlati