1. Achtergrondinformatie over hoe iterators zijn ontstaan

Je bent al bekend met HashSet. Als je het echt hebt onderzocht, afgezien van alleen het lezen van een les, dan had je deze vraag moeten stellen:

Hoe toon ik een lijst met alle HashSet-elementen op het scherm? De interface heeft immers geen get()en set()methoden!

En HashSetis niet de enige in deze beperking. Naast HashSet, zijn er veel andere collecties die het niet mogelijk maken om elementen op te halen via index, omdat de elementen geen gedefinieerde volgorde hebben.

Door de jaren heen hebben programmeurs veel complexe datastructuren uitgevonden, zoals grafieken en bomen. Of lijsten met lijsten.

Veel containers veranderen de volgorde van hun elementen wanneer nieuwe elementen worden toegevoegd of bestaande elementen worden verwijderd. Een lijst slaat bijvoorbeeld elementen in een bepaalde volgorde op en wanneer een nieuw element wordt toegevoegd, wordt het bijna altijd in het midden van de lijst ingevoegd.

En we krijgen ook situaties waarin er een container is die elementen opslaat, maar niet in een vaste volgorde.

Laten we nu zeggen dat we alle elementen uit zo'n verzameling naar een array of lijst willen kopiëren. We moeten alle elementen krijgen. Het maakt ons niet uit in welke volgorde we de elementen herhalen - het belangrijkste is om dezelfde elementen niet meer dan één keer te herhalen. Hoe doen we dat?


2. Iterator voor een verzameling

Iterators werden voorgesteld als een oplossing voor het bovenstaande probleem.

Een iterator is een speciaal object dat aan een verzameling is gekoppeld en dat helpt alle elementen van de verzameling te doorlopen zonder er een te herhalen.

U kunt de volgende code gebruiken om een ​​iterator voor elke verzameling te krijgen:

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

Waar nameis de naam van de verzamelingsvariabele, Typeis het type elementen van de verzameling, iterator()is een van de verzamelingsmethoden en itis de naam van de iteratorvariabele.

Een iterator-object heeft 3 methoden:

Methode Beschrijving
Type next()
Retourneert het volgende element in de verzameling
boolean hasNext()
Controleert of er elementen zijn die nog niet zijn doorlopen
void remove()
Verwijdert het huidige element van de collectie

Deze methoden lijken enigszins op die van de klasse Scanner nextInt)en hasNextInt()de methoden.

De next()methode retourneert het volgende element van de verzameling waaruit we de iterator hebben gehaald.

De hasNext()methode controleert of de verzameling aanvullende elementen bevat die de iterator nog niet heeft geretourneerd.

Zo kunt u alle elementen van een weergeven HashSet:

Code Notities
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);
}
Maak een HashSetobject dat Stringelementen opslaat.


We voegen begroetingen in verschillende talen toe aan de setvariabele.




Verkrijg een iterator-object voor de setset.
Zolang er nog elementen zijn

Pak het volgende element
Toon het element op het scherm


3. For-eachlus

Het grootste nadeel van een iterator is dat uw code omslachtiger wordt dan het gebruik van een forlus.

Laten we ter vergelijking een lijst weergeven met een forlus en ook met een iterator:

Iterator for loop
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, het is veel beter om de elementen van ArrayListeen lus te doorlopen — alles blijkt korter te zijn.

Maar de makers van Java besloten opnieuw wat suiker over ons heen te gieten. Gelukkig voor ons was het syntactische suiker .

Ze gaven Java een nieuw soort lus en noemden het een for-eachlus. Zo ziet het er in het algemeen uit:

for(Type name:collection)

Waar collectionis de naam van de verzamelingsvariabele, Typeis het type elementen in de verzameling en nameis de naam van een variabele die de volgende waarde uit de verzameling haalt bij elke iteratie van de lus.

Dit soort lus doorloopt alle elementen van een verzameling met behulp van een impliciete iterator. Dit is hoe het echt werkt:

Voor elke lus Wat de compiler ziet: Lus met een 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);
}

Wanneer de compiler een for-eachlus in uw code tegenkomt, vervangt hij deze eenvoudig door de code aan de rechterkant: hij voegt een aanroep toe om een ​​iterator te krijgen, samen met eventuele andere ontbrekende methodeaanroepen.

Programmeurs houden van de for-eachlus en gebruiken deze bijna altijd wanneer ze alle elementen van een verzameling moeten herhalen.

Zelfs het doorlopen van een ArrayListlijst met een for-eachlus ziet er korter uit:

Voor elke lus for loop
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);
}


for-each4. Een element in een lus verwijderen

De for-eachlus heeft één nadeel: hij kan elementen niet correct verwijderen. Als je op deze manier code schrijft, krijg je een foutmelding.

Code Opmerking
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);
}












De verwijderingsbewerking genereert een fout!

Dit is een erg mooie en begrijpelijke code, maar het zal niet werken.

Belangrijk!

U kunt een verzameling niet wijzigen terwijl u deze doorloopt met een iterator.

Er zijn drie manieren om deze beperking te omzeilen.

1. Gebruik een ander soort lus

When traversing an ArrayList collection, kunt u een gewone lus gebruiken met een itellervariabele.

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

Deze optie is echter niet geschikt voor HashSeten HashMapcollecties

2. Gebruik een expliciete iterator

U kunt expliciet een iterator gebruiken en de remove()methode ervan aanroepen.

Versie die werkt Versie die niet werkt
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 op dat we de remove()methode op het iterator-object aanroepen! De iterator is zich ervan bewust dat het item is verwijderd en kan de situatie correct afhandelen.

3. Gebruik een kopie van de collectie

U kunt ook een kopie van de verzameling maken en de kopie vervolgens in een for-eachlus gebruiken en elementen uit de oorspronkelijke verzameling verwijderen.

Code Opmerking
ArrayList<String> listCopy = new ArrayList(list);

for (String str: listCopy)
{
   if (str.equals("Hello"))
      list.remove(str);
}
Een kopie van een verzameling maken is supereenvoudig.



De lus gebruikt de iterator voor de kopie van de verzameling.
Elementen worden uit de collectie verwijderd list.

De verzameling wordt vrij snel gekopieerd, aangezien de elementen zelf niet worden gedupliceerd. In plaats daarvan slaat de nieuwe collectie verwijzingen op naar de elementen die al in de oude collectie bestaan.