Modi per scorrere su un elenco in Java

Essendo un po ‘nuovo per il linguaggio Java sto cercando di familiarizzare con tutti i modi (o almeno quelli non patologici) che si possa scorrere attraverso una lista (o forse altre collezioni) e i vantaggi o gli svantaggi di ciascuno.

Dato un object List list , conosco i seguenti modi per scorrere tutti gli elementi:

Basic for loop (ovviamente, ci sono anche while / do while loop)

 // Not recommended (see below)! for (int i = 0; i < list.size(); i++) { E element = list.get(i); // 1 - can call methods of element // 2 - can use 'i' to make index-based calls to methods of list // ... } 

Nota: Come sottolineato da @amarseillan, questa forma è una scelta sbagliata per iterare su List s, perché l’effettiva implementazione del metodo get potrebbe non essere efficiente come quando si usa un Iterator . Ad esempio, le implementazioni di LinkedList devono attraversare tutti gli elementi che precedono i per ottenere l’elemento i-esimo.

Nell’esempio precedente non è ansible per l’implementazione List “salvare la sua posizione” per rendere più efficienti le iterazioni future. Per un ArrayList non ha molta importanza, perché la complessità / il costo di get è il tempo costante (O (1)) mentre per un LinkedList è proporzionale alla dimensione della lista (O (n)).

Per ulteriori informazioni sulla complessità computazionale delle implementazioni di Collections predefinite, consulta questa domanda .

Enhanced for loop (ben spiegato in questa domanda )

 for (E element : list) { // 1 - can call methods of element // ... } 

Iterator

 for (Iterator iter = list.iterator(); iter.hasNext(); ) { E element = iter.next(); // 1 - can call methods of element // 2 - can use iter.remove() to remove the current element from the list // ... } 

ListIterator

 for (ListIterator iter = list.listIterator(); iter.hasNext(); ) { E element = iter.next(); // 1 - can call methods of element // 2 - can use iter.remove() to remove the current element from the list // 3 - can use iter.add(...) to insert a new element into the list // between element and iter->next() // 4 - can use iter.set(...) to replace the current element // ... } 

Java funzionale

 list.stream().map(e -> e + 1); // Can apply a transformation function for e 

Iterable.forEach , Stream.forEach , …

(Un metodo mappa dall’API Stream di Java 8 (vedere la risposta di @ i_am_zero).)

Nelle classi di raccolta Java 8 che implementano Iterable (ad esempio, tutti gli List ) ora hanno un metodo forEach , che può essere utilizzato al posto forEach for loop forEach sopra. (Ecco un’altra domanda che fornisce un buon confronto).

 Arrays.asList(1,2,3,4).forEach(System.out::println); // 1 - can call methods of an element // 2 - would need reference to containing object to remove an item // (TODO: someone please confirm / deny this) // 3 - functionally separates iteration from the action // being performsd with each item. Arrays.asList(1,2,3,4).stream().forEach(System.out::println); // Same capabilities as above plus potentially greater // utilization of parallelism // (caution: consequently, order of execution is not guaranteed, // see [Stream.forEachOrdered][stream-foreach-ordered] for more // information about this). 

Quali altri modi ci sono, se ce ne sono?

(A proposito, il mio interesse non deriva affatto dal desiderio di ottimizzare le prestazioni , voglio solo sapere quali forms sono disponibili per me come sviluppatore.)

Le tre forms di looping sono quasi identiche. Il ciclo perfezionato for :

 for (E element : list) { . . . } 

è, secondo la specifica del linguaggio Java , identico in effetti all’utilizzo esplicito di un iteratore con un ciclo for tradizionale. Nel terzo caso, è ansible modificare il contenuto della lista solo rimuovendo l’elemento corrente, e solo se lo si fa attraverso il metodo di remove dell’iteratore stesso. Con l’iterazione basata su indice, sei libero di modificare l’elenco in qualsiasi modo. Tuttavia, l’aggiunta o la rimozione di elementi che precedono l’indice corrente rischia di far saltare gli elementi del loop o di elaborare lo stesso elemento più volte; è necessario regolare l’indice del loop correttamente quando si apportano tali modifiche.

In tutti i casi, l’ element è un riferimento all’elemento della lista reale. Nessuno dei metodi di iterazione fa una copia di qualcosa nella lista. Le modifiche allo stato interno element verranno sempre visualizzate nello stato interno dell’elemento corrispondente nell’elenco.

In sostanza, esistono solo due modi per scorrere su un elenco: utilizzando un indice o utilizzando un iteratore. Il ciclo for enhanced è solo una scorciatoia sintattica introdotta in Java 5 per evitare il tedio di definire esplicitamente un iteratore. Per entrambi gli stili, si possono ottenere variazioni essenzialmente banali che usano for , while o do while blocchi, ma si riducono tutti alla stessa cosa (o, piuttosto, a due cose).

EDIT: Come @ iX3 indica in un commento, è ansible utilizzare un ListIterator per impostare l’elemento corrente di un elenco mentre si sta iterando. È necessario utilizzare List#listIterator() anziché List#iterator() per inizializzare la variabile loop (che, ovviamente, dovrebbe essere dichiarata come ListIterator anziché come Iterator ).

Esempio di ciascun tipo elencato nella domanda:

ListIterationExample.java

 import java.util.*; public class ListIterationExample { public static void main(String []args){ List numbers = new ArrayList(); // populates list with initial values for (Integer i : Arrays.asList(0,1,2,3,4,5,6,7)) numbers.add(i); printList(numbers); // 0,1,2,3,4,5,6,7 // replaces each element with twice its value for (int index=0; index < numbers.size(); index++) { numbers.set(index, numbers.get(index)*2); } printList(numbers); // 0,2,4,6,8,10,12,14 // does nothing because list is not being changed for (Integer number : numbers) { number++; // number = new Integer(number+1); } printList(numbers); // 0,2,4,6,8,10,12,14 // same as above -- just different syntax for (Iterator iter = numbers.iterator(); iter.hasNext(); ) { Integer number = iter.next(); number++; } printList(numbers); // 0,2,4,6,8,10,12,14 // ListIterator provides an "add" method to insert elements // between the current element and the cursor for (ListIterator iter = numbers.listIterator(); iter.hasNext(); ) { Integer number = iter.next(); iter.add(number+1); // insert a number right before this } printList(numbers); // 0,1,2,3,4,5,6,7,8,9,10,11,12,13,14,15 // Iterator provides a "remove" method to delete elements // between the current element and the cursor for (Iterator iter = numbers.iterator(); iter.hasNext(); ) { Integer number = iter.next(); if (number % 2 == 0) // if number is even iter.remove(); // remove it from the collection } printList(numbers); // 1,3,5,7,9,11,13,15 // ListIterator provides a "set" method to replace elements for (ListIterator iter = numbers.listIterator(); iter.hasNext(); ) { Integer number = iter.next(); iter.set(number/2); // divide each element by 2 } printList(numbers); // 0,1,2,3,4,5,6,7 } public static void printList(List numbers) { StringBuilder sb = new StringBuilder(); for (Integer number : numbers) { sb.append(number); sb.append(","); } sb.deleteCharAt(sb.length()-1); // remove trailing comma System.out.println(sb.toString()); } } 

Il ciclo di base non è raccomandato in quanto non si conosce l’implementazione della lista.

Se quella era una lista collegata, ogni chiamata a

 list.get(i) 

andrebbe iterando sulla lista, risultando in N ^ 2 di complessità temporale.

Un’iterazione in stile JDK8:

 public class IterationDemo { public static void main(String[] args) { List list = Arrays.asList(1, 2, 3); list.stream().forEach(elem -> System.out.println("element " + elem)); } } 

Non so cosa consideri patologico, ma lascia che ti fornisca alcune alternative che potresti non aver visto prima:

 List sl= list ; while( ! sl.empty() ) { E element= sl.get(0) ; ..... sl= sl.subList(1,sl.size()); } 

O la sua versione ricorsiva:

 void visit(List list) { if( list.isEmpty() ) return; E element= list.get(0) ; .... visit(list.subList(1,list.size())); } 

Inoltre, una versione ricorsiva del classico for(int i=0... :

 void visit(List list,int pos) { if( pos >= list.size() ) return; E element= list.get(pos) ; .... visit(list,pos+1); } 

Ne parlo perché sei “un po ‘nuovo per Java” e questo potrebbe essere interessante.

In Java 8 abbiamo diversi modi per iterare sulle classi di raccolta.

Utilizzando Iterable forEach

Le raccolte che implementano Iterable (ad esempio tutte le liste) ora hanno il metodo forEach . Possiamo usare il metodo-riferimento introdotto in Java 8.

 Arrays.asList(1,2,3,4).forEach(System.out::println); 

Utilizzo degli stream forEach e forEach Order

Possiamo anche scorrere su un elenco utilizzando Stream come:

 Arrays.asList(1,2,3,4).stream().forEach(System.out::println); Arrays.asList(1,2,3,4).stream().forEachOrdered(System.out::println); 

Preferiamo forEachOrdered over forEach perché il comportamento di forEach è esplicitamente non deterministico in cui come forEachOrdered esegue un’azione per ogni elemento di questo stream, nell’ordine di incontro del stream se il stream ha un ordine di incontro definito. Quindi forEach non garantisce che l’ordine venga mantenuto.

Il vantaggio con i flussi è che possiamo anche fare uso di flussi paralleli laddove appropriato. Se l’objective è solo stampare gli articoli indipendentemente dall’ordine, possiamo usare lo stream parallelo come:

 Arrays.asList(1,2,3,4).parallelStream().forEach(System.out::println); 

Puoi usare forEach a partire da Java 8:

  List nameList = new ArrayList<>( Arrays.asList("USA", "USSR", "UK")); nameList.forEach((v) -> System.out.println(v)); 

Di seguito sono riportati diversi modi per iterare un elenco in Java:

1- Tipico per loop

  private static void iterateListForLoop(List lstInput) { for(int i=0; i 

2- Migliorato per ciclo

Dal momento che JDK 5.0, è ansible iterare un elenco utilizzando un ciclo For avanzato che funziona come segue:

 private static void iterateListForEach(List lstInput) { for(String input : lstInput) { System.out.println(input); } } 

3- Ciclo tipico durante il ciclo

 private static void iterateListWhileLoop(List lstInput) { int i=0; while(i 

4- Iteratore

 private static void iterateListIterator(List lstInput) { for(Iterator iter = lstInput.iterator(); iter.hasNext();) { System.out.println(iter.next()); } } 

5- Java 8

Con Java 8, è ansible iterare un elenco in una riga utilizzando il metodo forEach () come segue:

 private static void iterateListJava8(List lstInput) { lstInput.forEach((name) -> System.out.println(name)); } 

Riferimento: come iterare un elenco in Java

Per una ricerca all’indietro dovresti usare quanto segue:

 for (ListIterator iterator = list.listIterator(list.size()); iterator.hasPrevious();) { SomeClass item = iterator.previous(); ... item.remove(); // For instance. } 

Se vuoi conoscere una posizione, usa iterator.previousIndex (). Aiuta anche a scrivere un ciclo interno che mette a confronto due posizioni nella lista (gli iteratori non sono uguali).

Giusto, sono elencate molte alternative. Il più semplice e più pulito sarebbe semplicemente usando l’enunciato potenziato come sotto. L’ Expression è di qualche tipo che è iterabile.

 for ( FormalParameter : Expression ) Statement 

Ad esempio, per scorrere gli id ​​List , possiamo semplicemente così,

 for (String str : ids) { // Do something } 

Si può sempre cambiare il primo e il terzo esempio con un ciclo while e un po ‘più di codice. Questo ti dà il vantaggio di poter usare il do-while:

 int i = 0; do{ E element = list.get(i); i++; } while (i < list.size()); 

Ovviamente, questo tipo di cose potrebbe causare una NullPointerException se list.size () restituisce 0, perché viene sempre eseguito almeno una volta. Questo può essere risolto testando se l'elemento è nullo prima di usare i suoi attributi / metodi. Tuttavia, è molto più semplice e facile usare il ciclo for