"Bună, Amigo!"

— Bună, Ellie!

„Astăzi vreau să vă vorbesc despre iteratori”.

"Iteratorii au fost inventați practic în același timp cu colecțiile. Scopul principal al colecțiilor este de a stoca elemente, iar scopul principal al unui iterator este de a recupera aceste elemente unul câte unul."

„Ce este atât de dificil în a obține un set de elemente?”

„În primul rând, elementele din unele colecții, cum ar fi Set, nu au o ordine stabilită și/sau ordinea se schimbă constant.”

„În al doilea rând, unele structuri de date pot stoca obiecte într-un mod foarte complex: în diferite grupuri, liste etc. Cu alte cuvinte, distribuirea în ordine a tuturor elementelor ar fi o sarcină non-trivială”.

„În al treilea rând, colecțiile tind să se schimbe. Să presupunem că decideți să afișați întregul conținut al unei colecții, dar chiar în mijlocul ieșirii JVM comută la un alt fir care înlocuiește jumătate din elementele colecției. Deci, în loc de ieșire, obțineți cine știe ce”.

"Hmm..."

„Dar! Acestea sunt exact genul de probleme pe care un iterator le poate rezolva. Un iterator este un obiect special dintr-o colecție care, pe de o parte, are acces la toate datele sale private și își cunoaște structura internă, iar pe de altă parte. , implementează interfața publică Iterator, care permite tuturor să știe cum să lucreze cu ea. "

„Unele iteratoare au o matrice internă în care toate elementele colecției sunt copiate atunci când este creat iteratorul. Acest lucru asigură că orice modificări ulterioare ale colecției nu vor afecta numărul sau ordinea elementelor.”

„Cred că ați întâlnit asta atunci când lucrați cu pentru fiecare . Nu puteți să treceți simultan peste o colecție și să eliminați elemente din ea. Totul este tocmai din cauza modului în care funcționează un iterator.”

„În noile colecții adăugate la biblioteca de concurență, iteratorul este reproiectat pentru a elimina această problemă.”

„Permiteți-mi să vă reamintesc cum funcționează un iterator.”

"Java are o interfață specială Iterator. Iată metodele sale:"

Metode ale interfeței Iterator<E> Descriere
boolean hasNext() Verifică dacă mai sunt elemente
E next() Returnează elementul curent și trece la următorul.
void remove() Îndepărtează elementul curent

„Un iterator vă permite să obțineți succesiv toate elementele unei colecții. Este mai logic să ne gândim la un iterator ca la ceva asemănător unui InputStream — are toate datele, dar sarcina lui este să le scoată secvenţial.”

„   Metoda următoare () returnează următorul element din colecție.”

„ Metoda hasNext () este folosită pentru a verifica dacă mai există elemente.”

„Și remove () elimină elementul curent”.

"Alte intrebari?"

"De ce metodele au nume atât de ciudate? De ce nu isEmpty() și getNextElement()?"

— Nu ar avea mai mult sens?

„Ar avea mai mult sens, dar numele provin din limbajul C++, unde iteratorii au apărut mai devreme”.

— Înțeleg. Hai să continuăm.

„Pe lângă un iterator, există și interfața Iterable, care trebuie implementată de toate colecțiile care acceptă iteratoare. Are o singură metodă:”

Metode ale interfeței Iterable<T> Descriere
Iterator<T>iterator() Returnează un obiect iterator

„Puteți folosi această metodă pe orice colecție pentru a face ca un obiect iterator să treacă prin elementele sale. Să trecem peste toate elementele dintr-un TreeSet :”

Exemplu
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}

„Folosirea unui iterator ca acesta nu este foarte convenabilă – există prea mult cod superflu și evident. Situația a devenit mai simplă când bucla for -each a apărut în Java.”

„Acum, acest cod este mult mai compact și mai ușor de citit:”

Inainte de După
TreeSet<String> set = new TreeSet<String>();
Iterator<String> iterator = set.iterator();

while (iterator.hasNext())
{
 String item = iterator.next();
 System.out.println(item);
}
TreeSet<String> set = new TreeSet<String>();

for(String item : set)
{
 System.out.println(item);
}

"Acesta este același cod! Iteratorul este folosit în ambele cazuri."

"Doar că utilizarea sa este ascunsă în bucla for-each . Rețineți că codul din dreapta nu are deloc text roșu . Utilizarea iteratorului este complet ascunsă."

„O buclă for-each poate fi folosită pentru orice obiecte care acceptă iteratoare. Cu alte cuvinte, puteți scrie propria clasă, adăugați metoda iterator () la ea și utilizați obiectele acesteia într-o construcție for-each .”

"Wow! Desigur, nu sunt nerăbdător să-mi scriu propriile colecții și iteratoare, dar perspectiva este încă tentantă. O să notez."

În plus, există un alt tip popular de iterator care are chiar și propria interfață. Vorbesc despre un iterator pentru liste, adică ListIterator .

„Indiferent de implementarea lor, listele mențin ordinea elementelor, ceea ce face lucrul cu ele printr-un iterator puțin mai convenabil.”

„Iată metodele interfeței ListIterator <E>:”

Metodă Descriere
boolean hasNext() Verifică dacă mai sunt elemente în față.
E next() Returnează următorul element.
int nextIndex() Returnează indexul următorului element
void set(E e) Modifică valoarea elementului curent
boolean hasPrevious() Verifică dacă există elemente în spate.
E previous() Returnează elementul anterior
int previousIndex() Returnează indexul elementului anterior
void remove() Îndepărtează elementul curent
void add(E e) Adaugă un element la sfârșitul listei.

"Cu alte cuvinte, aici ne putem deplasa atât înainte, cât și înapoi. Și există alte câteva caracteristici mici."

"Ei bine, sunt lucruri interesante. Unde se folosește?"

„Să presupunem că doriți să vă deplasați înainte și înapoi pe o listă legată. Operația de obținere va fi destul de lentă, dar operația următoare () va fi foarte rapidă.”

"Hmm. M-ai convins. O să țin cont."

— Mulțumesc, Ellie!