1. Bakgrund om hur iteratorer kom till

Du är redan bekant med HashSet. Om du verkligen har undersökt det, utöver att bara läsa en lektion, borde du ha ställt den här frågan:

Hur visar jag en lista över alla HashSet-element på skärmen? När allt kommer omkring, har gränssnittet inte get()och set()metoder!

Och HashSetär inte ensam om denna begränsning. Förutom , HashSetfinns det många andra samlingar som inte tillåter att element hämtas med index, eftersom elementen inte har någon definierad ordning.

Genom åren har programmerare uppfunnit massor av komplexa datastrukturer, som grafer och träd. Eller listor med listor.

Många behållare ändrar ordningen på sina element när nya element läggs till eller befintliga element tas bort. Till exempel lagrar en lista element i en viss ordning, och när ett nytt element läggs till infogas det nästan alltid i mitten av listan.

Och vi får även situationer där det finns en container som lagrar element men inte i någon fast ordning.

Låt oss nu säga att vi vill kopiera alla element från en sådan samling till en array eller lista. Vi måste få med oss ​​alla element. Vi bryr oss inte om i vilken ordning vi itererar över elementen - det viktiga är att inte iterera över samma element mer än en gång. Hur gör vi det?


2. Iterator för en samling

Iteratorer föreslogs som en lösning på problemet ovan.

En iterator är ett speciellt objekt som är associerat med en samling, som hjälper till att passera alla element i samlingen utan att upprepa några.

Du kan använda följande kod för att få en iterator för alla samlingar:

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

Var nameär namnet på samlingsvariabeln, Typeär typen av element i samlingen, iterator()är en av samlingens metoder och itär namnet på iteratorvariabeln.

Ett iteratorobjekt har tre metoder:

Metod Beskrivning
Type next()
Returnerar nästa element i samlingen
boolean hasNext()
Kontrollerar om det finns några element som inte har passerats ännu
void remove()
Tar bort det aktuella elementet i samlingen

Dessa metoder liknar i viss mån Scanner-klassens nextInt)och hasNextInt()metoderna.

Metoden next()returnerar nästa element i samlingen som vi fick iteratorn från.

Metoden hasNext()kontrollerar om samlingen har ytterligare element som iteratorn inte har returnerat ännu.

Så här visar du alla element i en HashSet:

Koda Anteckningar
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);
}
Skapa ett HashSetobjekt som lagrar Stringelement.


Vi lägger till hälsningar på olika språk till setvariabeln.




Skaffa ett iteratorobjekt för uppsättningen set.
Så länge det fortfarande finns element

Hämta nästa element
Visa elementet på skärmen


3. For-eachslinga

Den största nackdelen med en iterator är att din kod blir mer krånglig än att använda en forloop.

För att jämföra, låt oss visa en lista med en forloop och även med en iterator:

Iterator för slinga
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 är mycket bättre att gå igenom elementen i ArrayListen loop - allt visar sig vara kortare.

Men Javas skapare bestämde sig återigen för att hälla lite socker på oss. Lyckligtvis för oss var det syntaktisk socker .

De gav Java en ny typ av loop och kallade den en for-eachloop. Så här ser det ut i allmänhet:

for(Type name:collection)

Var collectionär namnet på samlingsvariabeln, Typeär typen av element i samlingen och nameär namnet på en variabel som tar nästa värde från samlingen vid varje iteration av loopen.

Denna typ av loop itererar genom alla element i en samling med hjälp av en implicit iterator. Så här fungerar det faktiskt:

För varje slinga Vad kompilatorn ser: Slinga 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 kompilatorn stöter på en for-eachloop i din kod, ersätter den den helt enkelt med koden till höger: den lägger till ett anrop för att få en iterator tillsammans med eventuella andra saknade metodanrop.

Programmerare älskar for-eachloopen och använder den nästan alltid när de behöver iterera över alla delar av en samling.

Även att iterera över en ArrayListlista med en for-eachloop ser kortare ut:

För varje slinga för slinga
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. Ta bort ett element i en for-eachslinga

Slingan for-eachhar en nackdel: den kan inte ta bort element korrekt. Om du skriver kod så här får du ett felmeddelande.

Koda Notera
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);
}












Ta bort operationen kommer att generera ett fel!

Detta är en mycket trevlig och begriplig kod, men den kommer inte att fungera.

Viktig!

Du kan inte ändra en samling medan du går igenom den med en iterator.

Det finns tre sätt att komma runt denna begränsning.

1. Använd en annan typ av slinga

When traversing an ArrayList collection, kan du använda en vanlig slinga med en iräknarvariabel.

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

Det här alternativet är dock inte lämpligt för HashSetoch HashMapsamlingar

2. Använd en explicit iterator

Du kan använda en iterator explicit och anropa dess remove()metod.

Version som fungerar En version som inte fungerar
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); }

Observera att vi anropar remove()metoden på iteratorobjektet! Iteratorn är medveten om att föremålet har tagits bort och kan hantera situationen korrekt.

3. Använd en kopia av samlingen

Du kan också skapa en kopia av samlingen och sedan använda kopian i en for-eachslinga och ta bort element från originalsamlingen.

Koda Notera
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Att skapa en kopia av en samling är superenkelt.



Slingan använder iteratorn för kopian av samlingen.
Element tas bort från listsamlingen.

Samlingen kopieras ganska snabbt, eftersom själva elementen inte är duplicerade. Den nya kollektionen lagrar istället referenser till de element som redan finns i den gamla kollektionen.