Wie man eine HashMap in Java iteriert
Java HashMap-Einträge mit entrySet, keySet, values, forEach und Streams iterieren – alle gängigen Methoden erklärt.
Eine HashMap speichert Schlüssel-Wert-Paare, und früher oder später muss man sie durchlaufen – um einen Bericht auszugeben, Werte zu summieren oder Einträge zu filtern. Java bietet mehrere idiomatische Möglichkeiten dafür, jede für einen leicht anderen Zweck geeignet: Braucht man Schlüssel, Werte oder beides? Dieses Kapitel behandelt die gebräuchlichen Ansätze – entrySet(), keySet(), values(), forEach und einen entfernenden Iterator – und erklärt, wann man welchen verwenden sollte.
Diese Techniken gelten für jede Map-Implementierung, einschließlich LinkedHashMap und TreeMap, da sie alle dieselbe Iterations-API teilen.
Einträge mit entrySet() durchlaufen
Wenn man sowohl den Schlüssel als auch den Wert benötigt, ist entrySet() die effizienteste Wahl. Es gibt eine Ansicht von Map.Entry-Objekten zurück, und ein einziger Durchlauf liefert jedes Paar ohne eine zweite Suche:
Map<String, Integer> stock = new HashMap<>();
for (Map.Entry<String, Integer> e : stock.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}Dies ist der empfohlene Standard. keySet() zu verwenden und dann innerhalb der Schleife map.get(key) aufzurufen, führt pro Element zu einer redundanten Hash-Suche; entrySet() vermeidet das vollständig.
Nur Schlüssel oder Werte iterieren
Wenn man nur eine Seite jedes Paares benötigt, fordert man genau diese Ansicht an. keySet() gibt die Schlüssel zurück, values() gibt die Werte zurück:
for (String key : stock.keySet()) {
System.out.println("key: " + key);
}
for (int qty : stock.values()) {
System.out.println("qty: " + qty);
}Beide Ansichten werden durch die Map unterstützt und spiegeln deren aktuellen Inhalt wider, ohne ihn zu kopieren. keySet() verwendet man, wenn die Werte wirklich nicht benötigt werden, und values(), wenn die Schlüssel irrelevant sind.
Die forEach-Methode
Seit Java 8 hat Map eine forEach-Methode, die einen BiConsumer entgegennimmt und Schlüssel und Wert als Lambda-Parameter bereitstellt. Sie ist prägnant und gut lesbar für einfache Seiteneffekte:
stock.forEach((key, value) -> System.out.println(key + "=" + value));Innerhalb eines Lambdas gibt es kein break oder continue, daher ist für ein frühzeitiges Beenden oder komplexe Kontrollflüsse eine klassische for-Schleife nach wie vor klarer.
Sicheres Entfernen mit einem Iterator
Das strukturelle Ändern einer Map, während eine for-each-Schleife darüber läuft, wirft eine ConcurrentModificationException. Um Einträge während des Durchlaufs zu entfernen, verwendet man einen expliziten Iterator und ruft dessen remove() auf:
Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue() < 10) {
it.remove();
}
}Eine moderne Alternative ist stock.entrySet().removeIf(e -> e.getValue() < 10), die denselben Filter in einer Zeile ausdrückt.
| Ansatz | Liefert | Am besten geeignet für |
|---|---|---|
entrySet() | Schlüssel + Wert | Standard; beide lesen |
keySet() | Nur Schlüssel | Arbeit mit Schlüsseln |
values() | Nur Werte | Summen, Wertdurchläufe |
forEach | Schlüssel + Wert (Lambda) | Prägnante Seiteneffekte |
Iterator | Schlüssel + Wert | Entfernen während des Durchlaufs |
Was man aus dem Programmlauf mitnehmen kann:
- Die
entrySet()-Schleife liest jeden Schlüssel und Wert in einem einzigen Durchlauf und akkumuliertTotal stock: 39, indem sie 12 + 7 + 20 summiert. keySet()gibt nur die Schlüssel aus (apple,banana,cherry), währendvalues()nur die Zahlen ausgibt, was zeigt, dass jede Ansicht eine Seite des Paares offenlegt.- Das
forEach-Lambda erzeugt dieselben Schlüssel=Wert-Zeilen wie die manuelle Schleife und bestätigt, dass es ein prägnantes Äquivalent für einfache Iterationen ist. - Es wurde eine
LinkedHashMapverwendet, damit die Ausgabe die Einfügereihenfolge beibehält – eine einfacheHashMapgibt keine Reihenfolgegarantie, sodass die Zeilen in beliebiger Reihenfolge erscheinen könnten. - Der
Iterator.remove()-Aufruf entferntbanana(Wert 7, unter 10) und hinterlässt{apple=12, cherry=20}, was eine sichere Löschung während der Schleife ohneConcurrentModificationExceptiondemonstriert.