2013-08-29 10 views
31

Si consideri lo scenario seguente.For-each vs Iterator. Quale sarà l'opzione migliore

List<String> list = new ArrayList<>(); 

Ora ho aggiunto i valori String per questo elenco.

Ho usato i seguenti modi per andare ogni singolo elemento nell'elenco.

Option One utilizzare for-each

for (String i : list) { 
     System.out.println(i); 
    } 

Opzione due utilizzare Iterator

Iterator it=list.iterator(); 
while (it.hasNext()){ 
    System.out.println(it.next()); 
} 

Voglio solo sapere c'è qualche vantaggio prestazionale se uso for-each invece di Iterator. Ed è anche una cattiva pratica usare Iterator ora un giorno in Java?

+4

credo che per ognuno utilizza l'iteratore –

+0

possibili duplicati http://stackoverflow.com/questions/2113216/which-is-more-efficient-a-for-each-loop-or-an-iterator –

risposta

68

for-each è zucchero sintattico per l'utilizzo di iterators (approccio 2).

Potrebbe essere necessario utilizzare iterators se è necessario modificare la raccolta nel ciclo. Il primo approccio farà eccezione.

for (String i : list) { 
    System.out.println(i); 
    list.remove(i); // throws exception 
} 

Iterator it=list.iterator(); 
while (it.hasNext()){ 
    System.out.println(it.next()); 
    it.remove(); // valid here 
} 
+7

Di Ovviamente è bene ricordare che l'aggiunta/rimozione di elementi dalla raccolta su cui si sta eseguendo il ciclo non è una buona pratica, proprio per la ragione che hai evidenziato. – Jeremy

1

Risposta semplice: No e no.

Internamente il ciclo for-each crea un Iterator per iterare attraverso la raccolta.

Il vantaggio di utilizzare lo Iterator in modo esplicito è che è possibile accedere al metodo Iterator s.

9

for-each è un costrutto di ciclo avanzato. Internamente crea un Iterator e itera sulla Collezione. L'unico vantaggio possibile dell'utilizzo di un oggetto Iterator effettivo rispetto al costrutto for-each consiste nel fatto che è possibile modificare la raccolta utilizzando i metodi di Iterator come .remove(). La modifica della raccolta senza utilizzare i metodi di Iterator durante l'iterazione produrrà un ConcurrentModificationException.

+2

* Can * not "will" ha portato a ConcurrentModificationException –

+0

Una confusione qui se è stata migliorata internamente per Iterator Iterator perché c'è una restrizione nella rimozione degli elementi durante l'iterazione? – shaun

+0

@shaun perché non hai accesso ad esso :) –

14

La differenza è in gran parte zucchero sintattico, tranne per il fatto che un Iterator può rimuovere elementi dalla Raccolta che sta iterando. Tecnicamente, il miglioramento dei loop consente di eseguire il loop su tutto ciò che è Iterable, che comprende almeno sia le raccolte che gli array.

Non preoccuparti delle differenze di prestazioni. Tale micro-ottimizzazione è una distrazione irrilevante. Se è necessario rimuovere gli oggetti man mano che si procede, utilizzare un Iterator.In caso contrario, per i cicli tendono ad essere utilizzati di più solo perché sono cioè più leggibile:

for (String s : stringList) { ... } 

vs:

for (Iterator<String> iter = stringList.iterator(); iter.hasNext();) { 
    String s = iter.next(); 
    ... 
} 
0

Se si desidera sostituire elementi nella tua lista, vorrei andare vecchia scuola con un per il ciclo

for (int nIndex=0; nIndex < list.size(); nIndex++) { 
    Obj obj = (Obj) list.get(nIndex); 

    // update list item 
    list.set(nIndex, obj2); 
} 
3
+0

perché pensi che sia meglio usare list.foreach()? in quanto tale metodo feaeach() non è possibile modificare una var extern e copiarla come finale o avvolgerla in una matrice. –

0

foreach utilizza iteratori sotto il cofano stesso. È davvero solo zucchero sintattico.

Si consideri il seguente programma:

import java.util.List; 
import java.util.ArrayList; 

public class Whatever { 
    private final List<Integer> list = new ArrayList<>(); 
    public void main() { 
     for(Integer i : list) { 
     } 
    } 
} 

Diamo compilare con javac Whatever.java,
E leggere il bytecode smontato di main(), utilizzando javap -c Whatever:

public void main(); 
    Code: 
    0: aload_0 
    1: getfield  #4     // Field list:Ljava/util/List; 
    4: invokeinterface #5, 1   // InterfaceMethod java/util/List.iterator:()Ljava/util/Iterator; 
    9: astore_1 
    10: aload_1 
    11: invokeinterface #6, 1   // InterfaceMethod java/util/Iterator.hasNext:()Z 
    16: ifeq   32 
    19: aload_1 
    20: invokeinterface #7, 1   // InterfaceMethod java/util/Iterator.next:()Ljava/lang/Object; 
    25: checkcast  #8     // class java/lang/Integer 
    28: astore_2 
    29: goto   10 
    32: return 

possiamo vedere che foreach compila fino a un programma che:

  • Crea iteratore utilizzando List.iterator()
  • Se Iterator.hasNext(): invoca Iterator.next() e continua ad anello

Per quanto riguarda "il motivo per cui non questo ciclo inutile ottenere ottimizzato fuori del codice compilato? possiamo vedere che non fa nulla con la voce dell'elenco ": beh, è ​​possibile codificare il tuo iterable tale che .iterator() ha effetti collaterali, o in modo che .hasNext() ha effetti collaterali o conseguenze significative

Si può facilmente immaginare che un iterabile che rappresenta una query scorrevole da un database possa fare qualcosa di drammatico su .hasNext() (come contattare il database o chiudere un cursore perché si è raggiunta la fine del set di risultati)

Quindi, anche anche se possiamo provare che non succede niente nel corpo del loop ... è più costoso (intrattabile?) dimostrare che non accade nulla di significativo/consequenziale quando iteriamo. Il compilatore deve lasciare questo corpo del ciclo vuoto nel programma

Il meglio che potessimo sperare sarebbe un compiler avviso.È interessante il fatto che javac -Xlint:all Whatever.java non sia non avvisarci su questo corpo del ciclo vuoto. IntelliJ IDEA però. Devo ammettere che ho configurato IntelliJ per usare Eclipse Compiler, ma questo potrebbe non essere il motivo per cui.

enter image description here

0

Ecco il codice semplice frammento per controllare le prestazioni di For-each vs Iterator vs for per l'attraversamento di ArrayList<String>, eseguita su versione Java 8.

 long MAX = 2000000; 

     ArrayList<String> list = new ArrayList<>(); 

     for (long i = 0; i < MAX; i++) { 

      list.add("" + i); 
     } 

     /** 
     * Checking with for each iteration. 
     */ 
     long A = System.currentTimeMillis(); 

     for (String data : list) { 
      // System.out.println(data); 
     } 

     long B = System.currentTimeMillis(); 
     System.out.println(B - A + "ms"); 

     /** 
     * Checking with Iterator method 
     */ 

     Iterator<String> iterator = list.iterator(); 
     while (iterator.hasNext()) { 
      iterator.next(); 
      // System.out.println(iterator.next()); 
     } 

     long C = System.currentTimeMillis(); 
     System.out.println(C - B + "ms"); 

     /** 
     * Checking with normal iteration. 
     */ 
     for (int i = 0; i < MAX; i++) { 
      list.get((int) (i % (MAX - i))); 
      // System.out.println(list.get(i)); 
     } 

     long D = System.currentTimeMillis(); 
     System.out.println(D - C + "ms"); 

Valori medi di uscita:

19ms 
9ms 
27ms 

analisi Risultato: Iterator (9 ms) < For-each (19ms) < For (27 ms)

Qui Iterator ha le migliori prestazioni e For ha il minor prestazioni. Tuttavia, le prestazioni di For-each si trovano da qualche parte nel mezzo.

Problemi correlati