Come Iterare una HashMap in Java
Itera le voci di una HashMap Java usando entrySet, keySet, values, forEach e stream.
Una HashMap memorizza coppie chiave-valore e prima o poi è necessario scorrerle - per stampare un report, sommare i valori o filtrare le voci. Java offre diversi modi idiomatici per farlo, ognuno adatto a un'esigenza leggermente diversa: vuoi le chiavi, i valori o entrambi? Questo capitolo tratta gli approcci più comuni - entrySet(), keySet(), values(), forEach e un Iterator con rimozione - e spiega quando usare ciascuno.
Queste tecniche si applicano a qualsiasi implementazione di Map, incluse LinkedHashMap e TreeMap, poiché condividono tutte la stessa API di iterazione.
Scorrere le voci con entrySet()
Quando hai bisogno sia della chiave che del valore, entrySet() è la scelta più efficiente. Restituisce una vista di oggetti Map.Entry e un singolo passaggio fornisce ogni coppia senza una seconda ricerca:
Map<String, Integer> stock = new HashMap<>();
for (Map.Entry<String, Integer> e : stock.entrySet()) {
System.out.println(e.getKey() + " -> " + e.getValue());
}Questo è il valore predefinito raccomandato. Usare keySet() e poi chiamare map.get(key) all'interno del ciclo esegue una ricerca hash ridondante per ogni elemento; entrySet() evita completamente questo problema.
Iterare solo chiavi o valori
Se ti interessa solo un lato di ogni coppia, richiedi solo quella vista. keySet() restituisce le chiavi e values() restituisce i valori:
for (String key : stock.keySet()) {
System.out.println("key: " + key);
}
for (int qty : stock.values()) {
System.out.println("qty: " + qty);
}Entrambe le viste sono supportate dalla mappa, quindi riflettono il contenuto corrente senza copiarlo. Usa keySet() quando non hai davvero bisogno dei valori e values() quando le chiavi sono irrilevanti.
Il metodo forEach
Da Java 8, Map dispone di un metodo forEach che accetta un BiConsumer, fornendoti chiave e valore come parametri lambda. È conciso e si legge bene per semplici effetti collaterali:
stock.forEach((key, value) -> System.out.println(key + "=" + value));Non esiste break o continue all'interno di una lambda, quindi per un'uscita anticipata o un flusso di controllo complesso un classico ciclo for rimane più chiaro.
Rimozione sicura con un Iterator
Modificare strutturalmente una mappa mentre un ciclo for-each la scorre lancia ConcurrentModificationException. Per rimuovere voci durante l'attraversamento, usa un Iterator esplicito e chiama il suo metodo remove():
Iterator<Map.Entry<String, Integer>> it = stock.entrySet().iterator();
while (it.hasNext()) {
if (it.next().getValue() < 10) {
it.remove();
}
}Un'alternativa moderna è stock.entrySet().removeIf(e -> e.getValue() < 10), che esprime lo stesso filtro in una riga.
| Approccio | Fornisce | Ideale per |
|---|---|---|
entrySet() | chiave + valore | predefinito; lettura di entrambi |
keySet() | solo chiavi | lavoro con le chiavi |
values() | solo valori | totali, scansioni di valori |
forEach | chiave + valore (lambda) | effetti collaterali concisi |
Iterator | chiave + valore | rimozione durante l'attraversamento |
Cosa osservare dall'esecuzione:
- Il ciclo
entrySet()legge ogni chiave e valore in un singolo passaggio e accumulaTotal stock: 39, sommando 12 + 7 + 20. keySet()stampa solo le chiavi (apple,banana,cherry) mentrevalues()stampa solo i numeri, dimostrando che ogni vista espone un solo lato della coppia.- La lambda
forEachproduce le stesse righe chiave=valore del ciclo manuale, confermando che è un equivalente conciso per una semplice iterazione. - È stata usata una
LinkedHashMapaffinché l'output mantenga l'ordine di inserimento - una sempliceHashMapnon garantisce alcun ordinamento, quindi le righe potrebbero apparire in qualsiasi sequenza. - La chiamata
Iterator.remove()eliminabanana(valore 7, inferiore a 10) e lascia{apple=12, cherry=20}, dimostrando un'eliminazione sicura all'interno del ciclo senzaConcurrentModificationException.