1. Bakgrunn om hvordan iteratorer ble til

Du er allerede kjent med HashSet. Hvis du virkelig har undersøkt det, utover å bare lese en leksjon, burde du ha stilt dette spørsmålet:

Hvordan viser jeg en liste over alle HashSet-elementer på skjermen? Tross alt har ikke grensesnittet get()og set()metoder!

Og HashSeter ikke alene om denne begrensningen. I tillegg til HashSet, er det mange andre samlinger som ikke tillater at elementer hentes etter indeks, fordi elementene ikke har noen definert rekkefølge.

Gjennom årene har programmerere funnet opp mange komplekse datastrukturer, som grafer og trær. Eller lister med lister.

Mange beholdere endrer rekkefølgen på elementene når nye elementer legges til eller eksisterende elementer fjernes. For eksempel lagrer en liste elementer i en bestemt rekkefølge, og når et nytt element legges til, settes det nesten alltid inn i midten av listen.

Og vi får også situasjoner der det er en container som lagrer elementer, men ikke i noen fast rekkefølge.

La oss nå si at vi ønsker å kopiere alle elementene fra en slik samling til en matrise eller liste. Vi må få med oss ​​alle elementene. Vi bryr oss ikke om rekkefølgen vi itererer over elementene - det viktigste er å ikke iterere over de samme elementene mer enn én gang. Hvordan gjør vi det?


2. Iterator for en samling

Iteratorer ble foreslått som en løsning på problemet ovenfor.

En iterator er et spesielt objekt knyttet til en samling, som hjelper til med å krysse alle elementene i samlingen uten å gjenta noen.

Du kan bruke følgende kode for å få en iterator for enhver samling:

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

Hvor nameer navnet på samlingsvariabelen, Typeer typen av elementene i samlingen, iterator()er en av samlingens metoder, og iter navnet på iteratorvariabelen.

Et iteratorobjekt har 3 metoder:

Metode Beskrivelse
Type next()
Returnerer neste element i samlingen
boolean hasNext()
Sjekker om det er noen elementer som ikke er krysset ennå
void remove()
Fjerner gjeldende element i samlingen

Disse metodene ligner noe på Scanner-klassens nextInt)og hasNextInt()-metodene.

Metoden next()returnerer det neste elementet i samlingen som vi fikk iteratoren fra.

Metoden hasNext()sjekker om samlingen har flere elementer som iteratoren ikke har returnert ennå.

Slik viser du alle elementene i en HashSet:

Kode Notater
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);
}
Lag et HashSetobjekt som lagrer Stringelementer.


Vi legger til hilsener på forskjellige språk til setvariabelen.




Få et iteratorobjekt for setsettet.
Så lenge det fortsatt er elementer

Hent neste element
Vis elementet på skjermen


3. For-eachløkke

Den største ulempen med en iterator er at koden din blir mer tungvint enn å bruke en forloop.

For å sammenligne, la oss vise en liste ved hjelp av en forløkke og også ved å bruke en iterator:

Iterator for løkke
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);
}

Ja, det er mye bedre å krysse elementene i en ArrayListløkke - alt viser seg å være kortere.

Men Javas skapere bestemte seg igjen for å helle litt sukker på oss. Heldigvis for oss var det syntaktisk sukker .

De ga Java en ny type loop og kalte den en for-eachloop. Slik ser det ut generelt:

for(Type name:collection)

Hvor collectioner navnet på samlingsvariabelen, Typeer typen av elementene i samlingen, og nameer navnet på en variabel som tar neste verdi fra samlingen ved hver iterasjon av løkken.

Denne typen loop itererer gjennom alle elementene i en samling ved å bruke en implisitt iterator. Dette er hvordan det faktisk fungerer:

For hver løkke Hva kompilatoren ser: Loop med en 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);
}

Når kompilatoren møter en for-eachløkke i koden din, erstatter den den ganske enkelt med koden til høyre: den legger til et kall for å få en iterator sammen med eventuelle andre manglende metodekall.

Programmerere elsker for-eachloopen og bruker den nesten alltid når de trenger å iterere over alle elementene i en samling.

Selv å iterere over en ArrayListliste ved hjelp av en for-eachloop ser kortere ut:

For hver løkke for løkke
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. Fjerne et element i en for-eachløkke

Løkken for-eachhar en ulempe: den kan ikke fjerne elementer på riktig måte. Hvis du skriver kode som dette, får du en feilmelding.

Kode Merk
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);
}












Fjernoperasjonen vil generere en feil!

Dette er en veldig fin og forståelig kode, men den vil ikke fungere.

Viktig!

Du kan ikke endre en samling mens du går gjennom den med en iterator.

Det er tre måter å omgå denne begrensningen på.

1. Bruk en annen type løkke

When traversing an ArrayList collection, kan du bruke en vanlig sløyfe med en itellervariabel.

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

Imidlertid er dette alternativet ikke egnet for HashSetog HashMapsamlinger

2. Bruk en eksplisitt iterator

Du kan bruke en iterator eksplisitt og kalle dens remove()metode.

Versjon som fungerer Versjon som ikke fungerer
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); }

Merk at vi kaller remove()metoden på iteratorobjektet! Iteratoren er klar over at varen er fjernet og kan håndtere situasjonen riktig.

3. Bruk en kopi av samlingen

Du kan også lage en kopi av samlingen og deretter bruke kopien i en for-eachloop og slette elementer fra den originale samlingen.

Kode Merk
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Å lage en kopi av en samling er superenkelt.



Lokken bruker iteratoren for kopien av samlingen.
Elementer fjernes fra listsamlingen.

Samlingen kopieres ganske raskt, siden selve elementene ikke er duplisert. I stedet lagrer den nye kolleksjonen referanser til elementene som allerede finnes i den gamle kolleksjonen.