1. Context despre cum au apărut iteratorii

Ești deja familiarizat cu HashSet. Dacă ai investigat cu adevărat, dincolo de doar citirea unei lecții, atunci ar fi trebuit să pui această întrebare:

Cum afișez pe ecran o listă cu toate elementele HashSet? La urma urmei, interfața nu are get()și set()metode!

Și HashSetnu este singurul în această limitare. Pe lângă HashSet, există multe alte colecții care nu permit recuperarea elementelor prin index, deoarece elementele nu au o ordine definită.

De-a lungul anilor, programatorii au inventat o mulțime de structuri complexe de date, cum ar fi grafice și arbori. Sau liste de liste.

Multe containere își schimbă ordinea elementelor atunci când sunt adăugate elemente noi sau sunt eliminate elementele existente. De exemplu, o listă stochează elemente într-o anumită ordine, iar atunci când este adăugat un element nou, acesta este aproape întotdeauna inserat în mijlocul listei.

Și avem și situații în care există un container care stochează elemente, dar nu într-o ordine fixă.

Acum să presupunem că vrem să copiem toate elementele dintr-o astfel de colecție într-o matrice sau listă. Trebuie să obținem toate elementele. Nu ne pasă de ordinea în care repetăm ​​elementele - important este să nu repetăm ​​aceleași elemente de mai multe ori. Cum facem asta?


2. Iterator pentru o colecție

Iteratorii au fost propuși ca soluție la problema de mai sus.

Un iterator este un obiect special asociat cu o colecție, care ajută la traversarea tuturor elementelor colecției fără a repeta niciuna.

Puteți folosi următorul cod pentru a obține un iterator pentru orice colecție:

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

Unde nameeste numele variabilei colecției, Typeeste tipul elementelor colecției, iterator()este una dintre metodele colecției și iteste numele variabilei iteratorului.

Un obiect iterator are 3 metode:

Metodă Descriere
Type next()
Returnează următorul element din colecție
boolean hasNext()
Verifică dacă există elemente care nu au fost încă traversate
void remove()
Îndepărtează elementul curent al colecției

nextInt)Aceste metode sunt oarecum similare cu metodele și clasele Scanner hasNextInt().

Metoda next()returnează următorul element al colecției din care am obținut iteratorul.

Metoda hasNext()verifică dacă colecția are elemente suplimentare pe care iteratorul nu le-a returnat încă.

Iată cum să afișați toate elementele unui HashSet:

Cod Note
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);
}
Creați un HashSetobiect care stochează Stringelemente.


Adăugăm în setvariabilă salutări în diferite limbi.




Obțineți un obiect iterator pentru setset.
Atâta timp cât mai există elemente

Obțineți următorul element
Afișați elementul pe ecran


3. For-eachbuclă

Principalul dezavantaj al unui iterator este că codul tău devine mai greoi decât utilizarea unei forbucle.

Pentru a compara, să afișăm o listă folosind o forbuclă și, de asemenea, folosind un iterator:

Iterator pentru buclă
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);
}

Da, este mult mai bine să traversați elementele unei ArrayListbucle folosind o buclă - totul se dovedește a fi mai scurt.

Dar creatorii lui Java au decis din nou să toarne niște zahăr peste noi. Din fericire pentru noi, a fost zahăr sintactic .

Au dat Java un nou tip de buclă și l-au numit for-eachbuclă. Cam asa arata in general:

for(Type name:collection)

Unde collectioneste numele variabilei de colecție, Typeeste tipul elementelor din colecție și nameeste numele unei variabile care ia următoarea valoare din colecție la fiecare iterație a buclei.

Acest tip de buclă iterează prin toate elementele unei colecții folosind un iterator implicit. Iată cum funcționează de fapt:

Pentru fiecare buclă Ce vede compilatorul: buclă cu un iterator
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);
}

Când compilatorul întâlnește o for-eachbuclă în codul dvs., pur și simplu o înlocuiește cu codul din dreapta: adaugă un apel pentru a obține un iterator împreună cu orice alte apeluri de metodă lipsă.

Programatorii iubesc for-eachbucla și aproape întotdeauna o folosesc atunci când trebuie să itereze peste toate elementele unei colecții.

Chiar și repetarea unei ArrayListliste folosind o for-eachbuclă pare mai scurtă:

Pentru fiecare buclă pentru buclă
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. Eliminarea unui element dintr-o for-eachbuclă

Bucla for-eachare un dezavantaj: nu poate elimina corect elementele. Dacă scrieți astfel de cod, veți primi o eroare.

Cod Notă
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);
}












Operația de eliminare va genera o eroare!

Acesta este un cod foarte frumos și de înțeles, dar nu va funcționa.

Important!

Nu puteți schimba o colecție în timp ce o parcurgeți cu un iterator.

Există trei moduri de a ocoli această limitare.

1. Folosiți un alt tip de buclă

When traversing an ArrayList collection, puteți utiliza o buclă obișnuită cu o ivariabilă contor.

Cod
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
   }
}

Cu toate acestea, această opțiune nu este potrivită pentru HashSetși HashMapcolecții

2. Utilizați un iterator explicit

Puteți utiliza un iterator în mod explicit și puteți apela remove()metoda acestuia.

Versiune care funcționează Versiune care nu merge
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); }

Rețineți că numim remove()metoda pe obiectul iterator! Iteratorul este conștient de faptul că elementul a fost eliminat și poate gestiona situația corect.

3. Folosiți o copie a colecției

De asemenea, puteți crea o copie a colecției și apoi utilizați copia într-o for-eachbuclă și ștergeți elemente din colecția originală.

Cod Notă
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Crearea unei copii a unei colecții este foarte ușoară.



Bucla folosește iteratorul pentru copia colecției.
Elementele sunt eliminate din listcolecție.

Colecția este copiată destul de repede, deoarece elementele în sine nu sunt duplicate. În schimb, noua colecție stochează referințe la elementele care există deja în vechea colecție.