1. Baggrund for, hvordan iteratorer blev til

Du er allerede bekendt med HashSet. Hvis du virkelig har undersøgt det, ud over blot at læse en lektion, så skulle du have stillet dette spørgsmål:

Hvordan viser jeg en liste over alle HashSet-elementer på skærmen? Efter alt, har grænsefladen ikke get()og set()metoder!

Og HashSeter ikke alene om denne begrænsning. Ud over HashSet, er der mange andre samlinger, der ikke tillader elementer at blive hentet efter indeks, fordi elementerne ikke har nogen defineret rækkefølge.

Gennem årene har programmører opfundet masser af komplekse datastrukturer, såsom grafer og træer. Eller lister med lister.

Mange containere ændrer rækkefølgen af ​​deres elementer, når nye elementer tilføjes eller eksisterende elementer fjernes. For eksempel gemmer en liste elementer i en bestemt rækkefølge, og når et nyt element tilføjes, indsættes det næsten altid i midten af ​​listen.

Og vi får også situationer, hvor der er en container, der opbevarer elementer, men ikke i nogen fast rækkefølge.

Lad os nu sige, at vi vil kopiere alle elementer fra en sådan samling til en matrix eller liste. Vi skal have alle elementerne med. Vi er ligeglade med den rækkefølge, vi itererer over elementerne i - det vigtige er ikke at iterere over de samme elementer mere end én gang. Hvordan gør vi det?


2. Iterator til en samling

Iteratorer blev foreslået som en løsning på problemet ovenfor.

En iterator er et særligt objekt forbundet med en samling, som hjælper med at krydse alle elementer i samlingen uden at gentage nogen.

Du kan bruge følgende kode til at få en iterator til enhver samling:

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

Hvor nameer navnet på samlingsvariablen, Typeer typen af ​​elementerne i samlingen, iterator()er en af ​​samlingens metoder og iter navnet på iteratorvariablen.

Et iteratorobjekt har 3 metoder:

Metode Beskrivelse
Type next()
Returnerer det næste element i samlingen
boolean hasNext()
Kontrollerer, om der er elementer, der ikke er gennemløbet endnu
void remove()
Fjerner det aktuelle element i samlingen

Disse metoder minder lidt om Scanner-klassens nextInt)og hasNextInt()-metoderne.

Metoden next()returnerer det næste element i samlingen, hvorfra vi fik iteratoren.

Metoden hasNext()kontrollerer, om samlingen har yderligere elementer, som iteratoren ikke har returneret endnu.

Sådan viser du alle elementerne i en HashSet:

Kode Noter
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);
}
Opret et HashSetobjekt, der gemmer Stringelementer.


Vi tilføjer hilsner på forskellige sprog til variablen set.




Få et iteratorobjekt til sættet set.
Så længe der stadig er elementer

Hent det næste element
Vis elementet på skærmen


3. For-eachsløjfe

Den største ulempe ved en iterator er, at din kode bliver mere besværlig end at bruge en forloop.

For at sammenligne, lad os vise en liste ved hjælp af en forloop og også ved hjælp af en iterator:

Iterator for sløjfe
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 meget bedre at krydse elementerne i en ArrayListløkke - alt viser sig at være kortere.

Men Javas skabere besluttede igen at hælde noget sukker på os. Heldigvis for os var det syntaktisk sukker .

De gav Java en ny slags loop og kaldte det en for-eachloop. Sådan ser det generelt ud:

for(Type name:collection)

Hvor collectioner navnet på samlingsvariablen, Typeer typen af ​​elementerne i samlingen og nameer navnet på en variabel, der tager den næste værdi fra samlingen ved hver iteration af løkken.

Denne form for sløjfe gentager alle elementerne i en samling ved hjælp af en implicit iterator. Sådan fungerer det faktisk:

For hver sløjfe Hvad compileren 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 compileren støder på en for-eachløkke i din kode, erstatter den den blot med koden til højre: den tilføjer et kald for at få en iterator sammen med eventuelle andre manglende metodekald.

Programmører elsker for-eachløkken og bruger den næsten altid, når de skal gentage alle elementerne i en samling.

Selv iteration over en ArrayListliste ved hjælp af en for-eachloop ser kortere ud:

For hver sløjfe for sløjfe
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. Fjernelse af et element i en for-eachløkke

Sløjfen for-eachhar én ulempe: den kan ikke fjerne elementer korrekt. Hvis du skriver kode som denne, får du en fejl.

Kode Bemærk
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);
}












Fjernelse vil generere en fejl!

Dette er en meget flot og forståelig kode, men den virker ikke.

Vigtig!

Du kan ikke ændre en samling, mens du krydser den med en iterator.

Der er tre måder at omgå denne begrænsning på.

1. Brug en anden slags løkke

When traversing an ArrayList collection, kan du bruge en almindelig sløjfe med en itællervariabel.

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

Denne mulighed er dog ikke egnet til HashSetog HashMapsamlinger

2. Brug en eksplicit iterator

Du kan bruge en iterator eksplicit og kalde dens remove()metode.

Version der virker En version der ikke virker
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); }

Bemærk, at vi kalder remove()metoden på iteratorobjektet! Iteratoren er klar over, at varen er blevet fjernet og kan håndtere situationen korrekt.

3. Brug en kopi af samlingen

Du kan også oprette en kopi af samlingen og derefter bruge kopien i en for-eachløkke og slette elementer fra den originale samling.

Kode Bemærk
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Det er super nemt at oprette en kopi af en samling.



Sløjfen bruger iteratoren til kopien af ​​samlingen.
Elementer fjernes fra listsamlingen.

Samlingen kopieres ret hurtigt, da selve elementerne ikke er duplikeret. I stedet gemmer den nye kollektion referencer til de elementer, der allerede findes i den gamle kollektion.