Java HashMap entrySet: Iterate Efficiently
Learn how to iterate over a Java HashMap with entrySet(). This guide covers for-each loops, safe removal with iterators, performance trade-offs, and common pitfalls.
The entrySet() method on a Java HashMap returns a Set view of the key-value mappings in the map. It is the most direct way to access both a key and its value during iteration. This article explains what entrySet() returns, how to iterate with it, how to modify entries safely, and when it is a better choice than iterating over keySet() or values().
What entrySet() Returns and Why It Matters
entrySet() returns a Set<Map.Entry<K, V>>. Each Map.Entry represents a single key-value pair and exposes getKey(), getValue(), and setValue(). The returned set is backed by the map, so changes you make through the set (such as removing an entry) are reflected in the original map. This live view is important for both iteration and modification.
Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 90); scores.put("Bob", 85); Set<Map.Entry<String, Integer>> entries = scores.entrySet(); for (Map.Entry<String, Integer> entry : entries) { System.out.println(entry.getKey() + ": " + entry.getValue()); }
The entrySet() method is defined in the Map interface, so it works for TreeMap, LinkedHashMap, and other map implementations. However, iteration order depends on the implementation. A HashMap does not guarantee order, while a LinkedHashMap preserves insertion order.
Basic Iteration with for-each
The most common pattern is a for-each loop over the entry set. This avoids the separate lookup that occurs when you iterate over keySet() and then call get() for each key.
for (Map.Entry<String, Integer> entry : map.entrySet()) { String key = entry.getKey(); Integer value = entry.getValue(); // process key and value }
This is concise and readable. The entry object is backed by the map, so calling setValue() on it updates the map. If you need to remove entries while iterating, use an explicit iterator and its remove() method, as shown below. If the map is structurally modified from another thread during iteration without proper synchronization, you may get a ConcurrentModificationException.
Modifying Entries During Iteration
Because each Map.Entry is backed by the map, you can update the value of an existing key without explicitly calling put() again. This is often more efficient because it avoids an extra hash lookup.
for (Map.Entry<String, Integer> entry : map.entrySet()) { if (entry.getValue() < 50) { entry.setValue(entry.getValue() + 10); } }
Removing entries during iteration is also possible using the iterator's remove() method. This is the safe way to remove while iterating; calling map.remove() directly from inside iteration would cause a ConcurrentModificationException.
Iterator<Map.Entry<String, Integer>> iterator = map.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); if (entry.getValue() == 0) { iterator.remove(); } }
The iterator's remove() method is backed by the map and maintains the iteration's integrity.
Performance: entrySet() vs keySet() vs values()
The choice of iteration method affects runtime cost, especially for large maps. The table below summarizes the key differences.
| Method | What you get | Extra lookup per entry | Best when |
|---|---|---|---|
entrySet() | Key and value | No | You need both key and value |
keySet() | Keys only | Yes (via get()) | You only need keys, or you need to modify values with put() |
values() | Values only | No | You only need values |
Iterating over keySet() and then calling get() for each key performs an additional hash lookup for every entry. In a typical HashMap, each lookup is roughly O(1), but the extra work still adds up. If you need both the key and the value, entrySet() avoids that extra lookup.
There is also a memory consideration: entrySet() returns a view, not a copy. It does not allocate a new collection. The same is true for keySet() and values(). So the performance difference is mainly the number of operations per iteration.
Common Pitfalls and Edge Cases
One common mistake is assuming that entrySet() returns a snapshot. It does not. The set is backed by the map, so structural modifications (adding or removing entries) outside of the iterator will cause a ConcurrentModificationException if they happen during iteration. This is fail-fast behavior designed to catch bugs early.
Another pitfall is calling entrySet() on a null map, which will throw a NullPointerException. Always ensure the map is non-null before calling entrySet().
When you only need to update values, you might be tempted to use keySet() and call put() for each key. This works, but put() performs a hash lookup for every entry, and if you also read the current value with get() first, that is another lookup. Using entrySet() with setValue() is more efficient and also expresses the intent more clearly.
If the map is very large and you only need the keys, iterating over keySet() is appropriate. But if you later need the value for each key, you will pay the extra lookup. Consider your actual data access pattern before choosing.
When to Use entrySet() Over Other Approaches
Use entrySet() when you need to read both keys and values, or when you need to modify values in place. It is the standard, idiomatic way to iterate over a map's contents. For Java 8 and later, you can also use forEach() with a lambda:
map.forEach((key, value) -> { System.out.println(key + " -> " + value); });
Map.forEach is a more functional alternative for reading both keys and values. However, it does not allow you to remove entries during iteration; use an explicit iterator for that.
If you need to iterate and remove entries conditionally, the iterator over entrySet() is the correct approach. If you need to transform the map into another structure, entrySet() provides a natural source for stream operations:
Map<String, Integer> doubled = map.entrySet().stream() .collect(Collectors.toMap( Map.Entry::getKey, e -> e.getValue() * 2 ));
This pattern is concise and avoids the overhead of a separate get() call. The choice between entrySet() and the other methods ultimately depends on whether you need both key and value, and whether you need to modify the map during iteration. In most cases, entrySet() is the safest and most efficient default.