Accedere ad una Collezione usando gli Iteratori in Java
- Gli iteratori forniscono un modo per accedere agli elementi di una collezione in modo sequenziale senza esporre la sua rappresentazione interna.
- Le interfacce
Iterator
eListIterator
consentono di scorrere gli elementi di una collezione, conListIterator
che offre funzionalità aggiuntive per l'attraversamento bidirezionale e la modifica degli elementi. - Gli iteratori possono essere utilizzati in modo più flessibile rispetto ai cicli tradizionali, consentendo di rimuovere elementi durante l'iterazione.
- Esiste un'alternativa più semplice agli iteratori, il ciclo for-each, che può essere utilizzato per scorrere le collezioni senza modificarle.
Accesso ad una Collezione con un Iterator
Spesso, sorge la necessità di scorrere gli elementi in una collezione.
Ad esempio, si potrebbe voler visualizzare ogni elemento. Un modo per farlo è utilizzare un iterator
, che è un oggetto che implementa l'interfaccia Iterator
o ListIterator
.
Iterator
vi consente di scorrere una collezione, ottenendo o rimuovendo elementi. ListIterator
estende Iterator
per consentire l'attraversamento bidirezionale di una lista e la modifica degli elementi. Iterator
e ListIterator
sono interfacce generiche che sono dichiarate come mostrato qui:
interface Iterator<E>
interface ListIterator<E>
Qui, E
specifica il tipo di oggetti su cui si vuole iterare.
L'interfaccia Iterator
dichiara i metodi mostrati nella tabella che segue:
Metodo | Descrizione |
---|---|
default void forEachRemaining(Consumer<? super E> action) |
L'azione specificata da action viene eseguita su ogni elemento non elaborato nella collezione. |
boolean hasNext() |
Restituisce true se ci sono più elementi. Altrimenti, restituisce false. |
E next() |
Restituisce l'elemento successivo. Lancia NoSuchElementException se non c'è un elemento successivo. |
default void remove() |
Rimuove l'elemento corrente. Lancia IllegalStateException se viene fatto un tentativo di chiamare remove() che non è preceduto da una chiamata a next() . La versione predefinita lancia una UnsupportedOperationException . |
I metodi dichiarati da ListIterator
sono mostrati nella tabella seguente:
Metodo | Descrizione |
---|---|
void add(E obj) |
Inserisce obj nella lista davanti all'elemento che sarà restituito dalla prossima chiamata a next() . |
default void forEachRemaining(Consumer<? super E> action) |
L'azione specificata da action viene eseguita su ogni elemento non elaborato nella collezione. |
boolean hasNext() |
Restituisce true se c'è un elemento successivo. Altrimenti, restituisce false . |
boolean hasPrevious() |
Restituisce true se c'è un elemento precedente. Altrimenti, restituisce false . |
E next() |
Restituisce l'elemento successivo. Viene lanciata una NoSuchElementException se non c'è un elemento successivo. |
int nextIndex() |
Restituisce l'indice dell'elemento successivo. Se non c'è un elemento successivo, restituisce la dimensione della lista. |
E previous() |
Restituisce l'elemento precedente. Viene lanciata una NoSuchElementException se non c'è un elemento precedente. |
int previousIndex() |
Restituisce l'indice dell'elemento precedente. Se non c'è un elemento precedente, restituisce –1. |
void remove() |
Rimuove l'elemento corrente dalla lista. Viene lanciata una IllegalStateException se remove() viene chiamato prima che next() o previous() vengano invocati. |
void set(E obj) |
Assegna obj all'elemento corrente. Questo è l'elemento restituito per ultimo da una chiamata a next() o previous() . |
In entrambi i casi, le operazioni che modificano la collezione sottostante sono opzionali. Ad esempio, remove()
lancerà UnsupportedOperationException
quando utilizzato con una collezione di sola lettura. Sono possibili varie altre eccezioni.
Utilizzare un Iterator
Prima di poter accedere ad una collezione tramite un iterator, occorre ottenerne uno.
Ciascuna delle classi collection fornisce un metodo iterator()
che restituisce un iterator all'inizio della collezione. Utilizzando questo oggetto iterator, è possibile accedere a ciascun elemento nella collezione, un elemento alla volta.
In generale, per utilizzare un iterator per scorrere il contenuto di una collezione, seguiamo questi passaggi:
-
- Otteniamo un iterator all'inizio della collezione chiamando il metodo
iterator()
della collezione.
- Otteniamo un iterator all'inizio della collezione chiamando il metodo
-
- Impostiamo un loop che effettua una chiamata a
hasNext()
. Facciamo iterare il loop finchéhasNext()
restituisce true.
- Impostiamo un loop che effettua una chiamata a
-
- All'interno del loop, otteniamo ciascun elemento chiamando
next()
.
- All'interno del loop, otteniamo ciascun elemento chiamando
Per le collezioni che implementano List
, possiamo anche ottenere un iterator chiamando listIterator()
. Come spiegato, un list iterator offre la capacità di accedere alla collezione in direzione sia avanti che indietro e permette di modificare un elemento. Per il resto, ListIterator
viene utilizzato proprio come Iterator
.
L'esempio seguente implementa questi passaggi, dimostrando sia le interfacce Iterator
che ListIterator
. Utilizza un oggetto ArrayList
, ma i principi generali si applicano a qualsiasi tipo di collezione. Naturalmente, ListIterator
è disponibile solo per quelle collezioni che implementano l'interfaccia List
.
// Dimostrare gli iterator.
import java.util.*;
class DemoIterator {
public static void main(String[] args) {
// Crea un array list.
ArrayList<String> al = new ArrayList<String>();
// Aggiunge elementi all'array list.
al.add("C");
al.add("A");
al.add("E");
al.add("B");
al.add("D");
al.add("F");
// Utilizza iterator per visualizzare il contenuto di al.
System.out.print("Contenuto originale di al: ");
Iterator<String> itr = al.iterator();
while (itr.hasNext()) {
String elemento = itr.next();
System.out.print(elemento + " ");
}
System.out.println();
// Modifica oggetti durante l'iterazione.
ListIterator<String> litr = al.listIterator();
while (litr.hasNext()) {
String elemento = litr.next();
litr.set(elemento + "+");
}
System.out.print("Contenuto modificato di al: ");
itr = al.iterator();
while (itr.hasNext()) {
String elemento = itr.next();
System.out.print(elemento + " ");
}
System.out.println();
// Ora, visualizza la lista all'indietro.
System.out.print("Lista modificata all'indietro: ");
while (litr.hasPrevious()) {
String elemento = litr.previous();
System.out.print(elemento + " ");
}
System.out.println();
}
}
L'output è mostrato qui:
Contenuto originale di al: C A E B D F
Contenuto modificato di al: C+ A+ E+ B+ D+ F+
Lista modificata all'indietro: F+ D+ B+ E+ A+ C+
Prestiamo particolare attenzione a come la lista viene visualizzata al contrario. Dopo che la lista è stata modificata, litr
punta alla fine della lista. Ricordiamo, litr.hasNext()
restituisce false
quando è stata raggiunta la fine della lista. Per attraversare la lista in reverse, il programma continua ad utilizzare litr
, ma questa volta controlla se ha un elemento precedente. Finché ne ha uno, quell'elemento viene ottenuto e visualizzato.
L'Alternativa For-Each agli Iteratori
Se non si modificano i contenuti di una collection o non si vuole ottenere gli elementi in ordine inverso, allora la versione for-each del ciclo for
è spesso un'alternativa più conveniente per scorrere una collection rispetto all'uso di un iteratore.
Si ricordi che il for
può scorrere qualsiasi collection di oggetti che implementa l'interfaccia Iterable
. Poiché tutte le classi collection implementano questa interfaccia, possono tutte essere elaborate dal for
.
L'esempio seguente usa un ciclo for
per sommare i contenuti di una collection:
// Usa il ciclo for-each per scorrere una collection.
import java.util.*;
class DemoForEach {
public static void main(String[] args) {
// Crea un array list per gli interi.
ArrayList<Integer> valori = new ArrayList<Integer>();
// Aggiungi valori all'array list.
valori.add(1);
valori.add(2);
valori.add(3);
valori.add(4);
valori.add(5);
// Usa il ciclo for per visualizzare i valori.
System.out.print("Contenuti di valori: ");
for (int v : valori)
System.out.print(v + " ");
System.out.println();
// Ora, somma i valori usando un ciclo for.
int somma = 0;
for (int v : valori)
somma += v;
System.out.println("Somma dei valori: " + somma);
}
}
L'output del programma è mostrato qui:
Contenuti di valori: 1 2 3 4 5
Somma dei valori: 15
Come potete vedere, il ciclo for
è sostanzialmente più breve e semplice da usare rispetto all'approccio basato su iteratore. Tuttavia, può essere usato solo per scorrere una collection nella direzione in avanti, e non potete modificare i contenuti della collection.