Una guida a Iterator in Java

1. Introduzione

Un Iterator è uno dei tanti modi in cui possiamo attraversare una raccolta e, come ogni opzione, ha i suoi pro e contro.

È stato introdotto per la prima volta in Java 1.2 in sostituzione di Enumerazioni e:

  • introdotto nomi di metodo migliorati
  • ha reso possibile rimuovere elementi da una raccolta su cui stiamo iterando
  • non garantisce l'ordine di iterazione

In questo tutorial, esamineremo la semplice interfaccia di Iterator per imparare come possiamo usare i suoi diversi metodi.

Verificheremo anche l' estensione ListIterator più robusta che aggiunge alcune funzionalità interessanti.

2. L' interfaccia Iterator

Per iniziare, dobbiamo ottenere un Iterator da una Collection ; questo viene fatto chiamando il metodo iterator () .

Per semplicità, otterremo l' istanza di Iterator da un elenco:

List items = ... Iterator iter = items.iterator();

L' interfaccia di Iterator ha tre metodi principali:

2.1. hasNext ()

Il metodo hasNext () può essere utilizzato per verificare se è rimasto almeno un elemento su cui eseguire l'iterazione.

È progettato per essere utilizzato come condizione nei cicli while :

while (iter.hasNext()) { // ... }

2.2. Il prossimo()

Il metodo next () può essere utilizzato per scavalcare l'elemento successivo e ottenerlo:

String next = iter.next();

È buona norma usare hasNext () prima di tentare di chiamare next () .

Gli iteratori per le raccolte non garantiscono l'iterazione in un ordine particolare a meno che non sia fornita da una particolare implementazione.

2.3. rimuovere()

Infine, se vogliamo rimuovere l'elemento corrente dalla raccolta, possiamo utilizzare il comando remove:

iter.remove();

Questo è un modo sicuro per rimuovere elementi durante l'iterazione su una raccolta senza il rischio di un'eccezione ConcurrentModificationException.

2.4. Esempio di iteratore completo

Ora possiamo combinarli tutti e dare un'occhiata a come utilizziamo i tre metodi insieme per il filtraggio delle raccolte:

while (iter.hasNext()) { String next = iter.next(); System.out.println(next); if( "TWO".equals(next)) { iter.remove(); } }

Questo è il modo in cui usiamo comunemente un iteratore, controlliamo in anticipo se c'è un altro elemento, lo recuperiamo e quindi eseguiamo alcune azioni su di esso.

2.5. Iterazione con espressioni Lambda

Come abbiamo visto negli esempi precedenti, è molto prolisso usare un Iterator quando vogliamo solo esaminare tutti gli elementi e fare qualcosa con essi.

A partire da Java 8, abbiamo il metodo forEachRemaining che consente l'uso di lambda per l'elaborazione degli elementi rimanenti:

iter.forEachRemaining(System.out::println);

3. L' interfaccia ListIterator

ListIterator è un'estensione che aggiunge nuove funzionalità per l'iterazione sugli elenchi:

ListIterator listIterator = items.listIterator(items.size());

Notate come possiamo fornire una posizione di partenza che in questo caso è la fine della lista.

3.1. hasPrevious () e previous ()

ListIterator può essere utilizzato per l'attraversamento all'indietro, quindi fornisce gli equivalenti di hasNext () e next () :

while(listIterator.hasPrevious()) { String previous = listIterator.previous(); }

3.2. nextIndex () e previousIndex ()

Inoltre, possiamo attraversare indici e non elementi reali:

String nextWithIndex = items.get(listIterator.nextIndex()); String previousWithIndex = items.get(listIterator.previousIndex());

Questo potrebbe rivelarsi molto utile nel caso in cui abbiamo bisogno di conoscere gli indici degli oggetti che stiamo attualmente modificando, o se vogliamo tenere un registro degli elementi rimossi.

3.3. Inserisci()

Il metodo add , che, come suggerisce il nome, ci permette di aggiungere un elemento prima dell'elemento che verrebbe restituito da next () e dopo quello restituito da previous ():

listIterator.add("FOUR");

3.4. impostato()

L'ultimo metodo degno di nota è set (), che ci permette di sostituire l'elemento che è stato restituito nella chiamata a next () o previous () :

String next = listIterator.next(); if( "ONE".equals(next)) { listIterator.set("SWAPPED"); }

È importante notare che questo può essere eseguito solo se non sono state effettuate chiamate precedenti a add () o remove () .

3.5. Esempio ListIterator completo

Ora possiamo combinarli tutti per fare un esempio completo:

ListIterator listIterator = items.listIterator(); while(listIterator.hasNext()) { String nextWithIndex = items.get(listIterator.nextIndex()); String next = listIterator.next(); if("REPLACE ME".equals(next)) { listIterator.set("REPLACED"); } } listIterator.add("NEW"); while(listIterator.hasPrevious()) { String previousWithIndex = items.get(listIterator.previousIndex()); String previous = listIterator.previous(); System.out.println(previous); }

In questo esempio, iniziamo ottenendo ListIterator da List , quindi possiamo ottenere l'elemento successivo tramite index, che non aumenta l'elemento corrente interno dell'iteratore , o chiamando next .

Quindi possiamo sostituire un articolo specifico con set e inserirne uno nuovo con aggiungi.

Dopo aver raggiunto la fine dell'iterazione, possiamo tornare indietro per modificare elementi aggiuntivi o semplicemente stamparli dal basso verso l'alto.

4. Conclusione

L' interfaccia Iterator ci permette di modificare una collezione mentre la attraversiamo, il che è più difficile con una semplice istruzione for / while. Questo, a sua volta, ci fornisce un buon modello che possiamo utilizzare in molti metodi che richiedono solo l'elaborazione delle raccolte mantenendo una buona coesione e un basso accoppiamento.

Infine, come sempre il codice sorgente completo è disponibile su GitHub.