Loop over entrySet(): for (Map.Entry<String, Integer> e : map.entrySet()), then read e.getKey() and e.getValue(). Each entry already holds its value, so there is no second lookup. On Java 8+, map.forEach((k, v) -> …) is a shorter option when you don't need break.
A Map isn't Iterable, so you can't write for (x : map). Instead it gives you three views to loop over: entrySet() for key-value pairs, keySet() for keys and values() for values. Java 8 added Map.forEach and streams on top of those. This page covers each of them, why looping over the keys and calling get() costs more than it looks, the order each Map implementation gives you, and how to remove entries without a ConcurrentModificationException. Each example runs on this page: hit Run, then edit the code and run it again.
1for-each over entrySet()Recommended
entrySet() returns a Set<Map.Entry<K, V>> backed by the map. An enhanced for loop over it gives you one entry per pair, with getKey() and getValue() on each. It works on every Java version, on every Map implementation, and break and continue behave as usual. On Java 10+, var saves you from spelling out the entry type.
Output
Prints apple -> 12, banana -> 0, cherry -> 7, then units: 19, then {apple=24, banana=0, cherry=14}. The last loop shows something keySet() can't do: entry.setValue() updates the value in place during iteration, which is allowed because it doesn't add or remove keys. The example uses a LinkedHashMap so the output order matches the order the keys were added; section 4 explains why a HashMap makes no such promise.
2Map.forEach((k, v) -> …)
Java 8 added Map.forEach, which takes a BiConsumer and calls it with the key and the value of every entry. It is the shortest way to visit a map, and implementations such as HashMap walk their internal table directly instead of creating an iterator. The trade-off is that it's a lambda: there is no break, and it can't reassign local variables from the surrounding method.
Output
Prints http = 80, https = 443, ssh = 22, then web: http and web: https, then total: 545. Inside the lambda, return acts like continue, not like break. A lambda also can't throw a checked exception such as IOException unless it catches it itself. Use the entrySet() loop when you need to stop early, update a local counter or let a checked exception propagate; use forEach for short side effects like printing or copying into another collection.
3keySet() and values(), and why keySet() + get() is wasteful
When you need only one side, loop over keySet() or values(). Both are live views of the map, not copies. What you should avoid is the pattern many people write first: loop over keySet() and call map.get(key) inside. Every get() is a fresh lookup for a value the iterator was already standing on. The snippet below counts the get() calls to show the difference.
Output
The keys print as fruit: apple, fruit: banana, fruit: cherry, then units: 19, values: [12, 0, 7]. Both loops reach the same total, but the last two lines differ: keySet() + get(): total 19, lookups 3 against entrySet(): total 19, lookups 0. On a HashMap each extra lookup rehashes the key and compares it with equals(); on a TreeMap each one is an O(log n) tree search, so the whole loop becomes O(n log n) instead of O(n). One catch with for (int n : map.values()): a null value throws a NullPointerException when it is unboxed, so loop with Integer if the map can hold nulls.
4Iteration order: HashMap vs LinkedHashMap vs TreeMap
Every loop on this page visits entries in whatever order the map implementation defines, and the three common ones differ. HashMap promises no order at all. LinkedHashMap iterates in insertion order. TreeMap iterates in sorted key order (or by the Comparator you give it). The immutable maps from Map.of and Map.copyOf are stricter still: their order is randomized for each run of the JVM, so the same program can print them differently twice in a row.
Output
Prints LinkedHashMap: [zulu, alpha, mike, bravo] and TreeMap: [alpha, bravo, mike, zulu]. The HashMap line prints false here, but the point is that nothing guarantees either answer: its order depends on hash codes and table size and can change as the map grows. Copying it into a TreeMap gives {alpha=5, bravo=5, mike=4, zulu=4} on every run. The Java 21 reversed() view prints [bravo, mike, alpha, zulu] and firstEntry()/lastEntry() print zulu=4 bravo=5. The TreeMap goes backwards with descendingMap() ([zulu, mike, bravo, alpha]) and subMap("b", "n") keeps keys from b up to, but not including, n: [bravo, mike].
5Removing entries while iterating
Calling map.remove() inside a for-each loop over the map changes it behind the iterator's back, and the next step of the loop throws ConcurrentModificationException. There are two correct ways. On Java 8+, call removeIf on the view you care about: entrySet(), keySet() or values(). On any version, use an explicit Iterator and call it.remove(), which is also the way to go when the loop does more than just remove.
Output
The broken loop prints for-each + remove: ConcurrentModificationException, and the next line is the worse part: left half-done: {alice=90, carol=72, dana=30}. The exception came after bob was already removed, so the map is left partly filtered. removeIf gives {alice=90, carol=72} in one line. The iterator loop prints dropping bob and dropping dana before arriving at the same map, and replaceAll then updates every value in place: {alice=95, carol=77}. The same rules apply to lists; see remove items from a list while iterating.
6Streams over entries
map.entrySet().stream() turns the entries into a stream, so filtering, sorting and collecting become one pipeline. Map.Entry.comparingByKey() and Map.Entry.comparingByValue() give you ready-made comparators. If you collect back into a map, pass LinkedHashMap::new to Collectors.toMap; the default is a HashMap, which throws away the order you just sorted into.
Output
Prints [go=98, rust=124, java=110], then rust 124, java 110, go 98, zig 35, then {rust=124, java=110} and 367 votes: go, rust, zig, java. The (a, b) -> a argument is the merge function toMap requires before it will accept a map factory; it only matters if two entries map to the same key. On Java 8 to 15, write .collect(Collectors.toList()) instead of .toList(). For more on ordering a map by its values, see sort a map by value.
7Which should you use?
| Method | Gives you | break / checked exceptions | Best for |
|---|---|---|---|
| for (var e : map.entrySet()) | Key and value | Both work | The default loop, any Java version |
| map.forEach((k, v) -> …) | Key and value | Neither | Short side effects, Java 8+ |
| for (K k : map.keySet()) | Keys only | Both work | When you never need the value |
| for (V v : map.values()) | Values only | Both work | Sums, counts, scans over values |
| keySet() + map.get(k) | Key and value | Both work | Avoid: one extra lookup per entry |
| entrySet().removeIf(…) / Iterator | Removal | Iterator: both work | Deleting entries while you iterate |
| map.entrySet().stream() | A pipeline | Neither | Filtering, sorting, collecting |
Frequently asked questions
What is the most efficient way to iterate over a HashMap in Java?
A for-each loop over map.entrySet(), or map.forEach((k, v) -> ...) on Java 8+. Both visit each entry once and hand you the value directly. Looping over keySet() and calling map.get(key) inside does one extra hash lookup per entry, and on a TreeMap each of those lookups is an O(log n) search. If you only need the keys or only the values, loop over keySet() or values().
Why does my HashMap print in a different order than I inserted?
Because HashMap does not keep insertion order; it iterates in the order of its internal hash buckets, which depends on the keys’ hash codes and the table size. Use LinkedHashMap for insertion order or TreeMap for sorted key order. The maps returned by Map.of and Map.copyOf go further: their iteration order is randomized per JVM run, so the same program can print [a, b, c, d, e] on one run and [e, d, c, b, a] on the next.
How do I remove entries from a Map while iterating over it?
Use map.entrySet().removeIf(e -> condition) on Java 8+, or keySet().removeIf / values().removeIf when you only need one side. On older versions, or when the loop does more than remove, iterate with an explicit Iterator and call it.remove(). Calling map.remove(key) inside a for-each loop throws ConcurrentModificationException, and the entries removed before the exception stay removed.
Can I update values while iterating over a Map?
Yes. Changing a value is not a structural change, so entry.setValue(newValue) inside an entrySet() loop is safe and needs no second lookup. To transform every value at once, use map.replaceAll((k, v) -> newValue) on Java 8+. What you must not do in a for-each loop is add or remove keys through the map itself.
Can I destructure a Map.Entry into key and value variables in the loop header?
No. Java has no syntax like for (var (k, v) : map.entrySet()), and record patterns do not apply because Map.Entry is an interface, not a record. Write for (var e : map.entrySet()) and call e.getKey() and e.getValue(), or use map.forEach((k, v) -> ...), which is the closest thing to named key and value variables.
How do I iterate over a ConcurrentHashMap while other code modifies it?
Just loop over it. The iterators of ConcurrentHashMap never throw ConcurrentModificationException, so removing an entry with map.remove(key) inside a for-each loop over its keySet() works. They are weakly consistent: changes made by other threads during the loop may or may not show up in it, so treat what you see as a snapshot that can be slightly out of date.