1. Background su come sono nati gli iteratori

Hai già familiarità con HashSet. Se l'hai davvero indagato, oltre a leggere una lezione, allora avresti dovuto porre questa domanda:

Come faccio a visualizzare un elenco di tutti gli elementi HashSet sullo schermo? Dopotutto, l'interfaccia non ha get()e set()metodi!

E HashSetnon è solo in questa limitazione. Oltre a HashSet, esistono molte altre raccolte che non consentono il recupero degli elementi in base all'indice, perché gli elementi non hanno un ordine definito.

Nel corso degli anni, i programmatori hanno inventato molte strutture dati complesse, come grafici e alberi. O liste di liste.

Molti contenitori cambiano l'ordine dei propri elementi quando vengono aggiunti nuovi elementi o vengono rimossi elementi esistenti. Ad esempio, un elenco memorizza gli elementi in un ordine particolare e quando viene aggiunto un nuovo elemento, viene quasi sempre inserito al centro dell'elenco.

E otteniamo anche situazioni in cui è presente un contenitore che memorizza elementi ma non in un ordine fisso.

Supponiamo ora di voler copiare tutti gli elementi da una tale raccolta in un array o in un elenco. Dobbiamo ottenere tutti gli elementi. Non ci interessa l'ordine in cui iteriamo sugli elementi: l'importante è non iterare sugli stessi elementi più di una volta. Come lo facciamo?


2. Iteratore per una raccolta

Gli iteratori sono stati proposti come soluzione al problema di cui sopra.

Un iteratore è un oggetto speciale associato a una raccolta, che consente di attraversare tutti gli elementi della raccolta senza ripeterne nessuno.

È possibile utilizzare il seguente codice per ottenere un iteratore per qualsiasi raccolta:

Iterator<Type> it = name.iterator();

Dove nameè il nome della variabile della raccolta, Typeè il tipo degli elementi della raccolta, iterator()è uno dei metodi della raccolta ed itè il nome della variabile iteratore.

Un oggetto iteratore ha 3 metodi:

Metodo Descrizione
Type next()
Restituisce l'elemento successivo nella raccolta
boolean hasNext()
Controlla se ci sono elementi che non sono stati ancora attraversati
void remove()
Rimuove l'elemento corrente della raccolta

nextInt)Questi metodi sono in qualche modo simili ai metodi e alla classe Scanner hasNextInt().

Il next()metodo restituisce l'elemento successivo della raccolta da cui abbiamo ottenuto l'iteratore.

Il hasNext()metodo controlla se la raccolta ha elementi aggiuntivi che l'iteratore non ha ancora restituito.

Ecco come visualizzare tutti gli elementi di un HashSet:

Codice Appunti
HashSet<String> set = new HashSet<String>();

set.add("Hallo");
set.add("Hello");
set.add("Hola");
set.add("Bonjour");
set.add("Ciao");
set.add("Namaste");

Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
Creare un HashSetoggetto che memorizzi Stringelementi.


Aggiungiamo saluti in varie lingue alla setvariabile.




Ottieni un oggetto iteratore per il setset.
Finché ci sono ancora elementi

Ottieni l'elemento successivo
Visualizza l'elemento sullo schermo


3. For-eachciclo

Il principale svantaggio di un iteratore è che il tuo codice diventa più ingombrante rispetto all'utilizzo di un forciclo.

Per confrontare, visualizziamo un elenco usando un forciclo e anche usando un iteratore:

Iteratore per ciclo
ArrayList<String> list = new ArrayList<String>();

Iterator<String> it = list.iterator();
while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}

Sì, è molto meglio attraversare gli elementi di un ArrayListusando un loop: tutto risulta essere più breve.

Ma i creatori di Java hanno nuovamente deciso di versarci un po' di zucchero. Fortunatamente per noi, era lo zucchero sintattico .

Hanno dato a Java un nuovo tipo di ciclo e lo hanno chiamato for-eachciclo. Ecco come appare in generale:

for(Type name:collection)

Dove collectionè il nome della variabile della raccolta, Typeè il tipo degli elementi nella raccolta ed nameè il nome di una variabile che prende il valore successivo dalla raccolta a ogni iterazione del ciclo.

Questo tipo di ciclo scorre tutti gli elementi di una raccolta utilizzando un iteratore implicito. Ecco come funziona effettivamente:

Per ogni ciclo Cosa vede il compilatore: Loop con un iteratore
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();
Iterator<String> it = list.iterator();

while (it.hasNext())
{
   String str = it.next();
   System.out.println(str);
}

Quando il compilatore incontra un for-eachciclo nel codice, lo sostituisce semplicemente con il codice a destra: aggiunge una chiamata per ottenere un iteratore insieme a qualsiasi altra chiamata al metodo mancante.

I programmatori adorano il for-eachciclo e lo usano quasi sempre quando devono eseguire iterazioni su tutti gli elementi di una raccolta.

Anche l'iterazione su un ArrayListelenco utilizzando un for-eachciclo sembra più breve:

Per ogni ciclo per ciclo
ArrayList<String> list = new ArrayList<String>();

for (String str: list)
{
   System.out.println(str);
}
ArrayList<String> list = new ArrayList<String>();

for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);
   System.out.println(str);
}


4. Rimozione di un elemento in un for-eachciclo

Il for-eachciclo ha uno svantaggio: non può rimuovere correttamente gli elementi. Se scrivi codice come questo, otterrai un errore.

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

list.add("Hallo");
list.add("Hello");
list.add("Hola");
list.add("Bonjour");
list.add("Ciao");
list.add("Namaste");

for (String str: list)
{
   if (str.equals("Hello"))
      list.remove(str);
}












L'operazione di rimozione genererà un errore!

Questo è un codice molto bello e comprensibile, ma non funzionerà.

Importante!

Non puoi modificare una raccolta mentre la stai attraversando con un iteratore.

Ci sono tre modi per aggirare questa limitazione.

1. Usa un diverso tipo di loop

When traversing an ArrayList collection, puoi utilizzare un normale ciclo con una ivariabile contatore.

Codice
for (int i = 0; i < list.size(); i++)
{
   String str = list.get(i);

   if (str.equals("Hello"))
   {
      list.remove(str);
      i--; // We need to decrease i, because the remove operation shifted the elements
   }
}

Tuttavia, questa opzione non è adatta per HashSete HashMapcollezioni

2. Utilizzare un iteratore esplicito

È possibile utilizzare un iteratore in modo esplicito e chiamarne il remove()metodo.

Versione che funziona Versione che non funziona
Iterator<String> it = set.iterator();
while (it.hasNext())
{
   String str = it.next();
   if (str.equals("Hello"))
       it.remove();
}

for (String str: list) { if (str.equals("Hello")) list.remove(str); }

Nota che chiamiamo il remove()metodo sull'oggetto iteratore! L'iteratore sa che l'elemento è stato rimosso e può gestire correttamente la situazione.

3. Usa una copia della raccolta

Puoi anche creare una copia della raccolta e quindi utilizzare la copia in un for-eachciclo ed eliminare elementi dalla raccolta originale.

Codice Nota
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Creare una copia di una raccolta è semplicissimo



Il ciclo utilizza l'iteratore per la copia della raccolta.
Gli elementi vengono rimossi dalla listraccolta.

La raccolta viene copiata piuttosto rapidamente, poiché gli elementi stessi non vengono duplicati. Invece, la nuova raccolta memorizza i riferimenti agli elementi che già esistono nella vecchia raccolta.