Back to Blog
Java

Java Map entrySet() for Iteration and Modification

java map entryset: Learn how to use Map.entrySet() to iterate over key-value pairs, modify entries, and understand performance tradeoffs.

JavaMapentrySetiterationperformanceconcurrency
Diagram of a Java Map with key-value pairs being iterated through entrySet, showing direct access to each entry.

java map entryset requires a clear understanding of the core syntax, runtime behavior, and practical implementation patterns demonstrated in the examples below.

The entrySet() method on java.util.Map returns a Set of Map.Entry objects, each representing a key-value pair. This is the most direct way to access both keys and values during iteration. Here's a minimal example:

Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 90); scores.put("Bob", 85); for (Map.Entry<String, Integer> entry : scores.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); }

The entrySet() view is backed by the map, so changes you make through the view are reflected in the original map. This is particularly useful when you need to update values while iterating.

What entrySet() Returns and Why It Matters

entrySet() returns a Set<Map.Entry<K, V>>. The Map.Entry interface provides getKey(), getValue(), and setValue(). The returned set is a view, not a copy. Modifying the set (e.g., removing an entry via Iterator.remove()) modifies the underlying map. This behavior is consistent across most Map implementations, including HashMap, TreeMap, and LinkedHashMap.

Understanding that entrySet() is a view is critical. If you call entrySet() twice, you get two different Set instances, but both reflect the same map state. This means you can iterate and modify entries without needing a separate copy.

Iterating Over a Map with entrySet()

The most common pattern is the enhanced for loop shown above. You can also use an explicit iterator:

Iterator<Map.Entry<String, Integer>> iterator = scores.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); // process entry }

The explicit iterator is useful when you need to remove entries during iteration. The Iterator.remove() method is safe and does not throw ConcurrentModificationException because it updates the map's modification count correctly.

Iterator<Map.Entry<String, Integer>> iterator = scores.entrySet().iterator(); while (iterator.hasNext()) { Map.Entry<String, Integer> entry = iterator.next(); if (entry.getValue() < 60) { iterator.remove(); // safe removal } }

This is the recommended way to remove entries while iterating, as opposed to calling map.remove() inside the loop, which would cause a ConcurrentModificationException in most implementations.

Modifying Values During Iteration

The setValue() method on Map.Entry allows you to update the value associated with the current key. This is more efficient than calling map.put(key, newValue) because it avoids a second lookup for the key.

for (Map.Entry<String, Integer> entry : scores.entrySet()) { if (entry.getValue() < 70) { entry.setValue(entry.getValue() + 10); // boost low scores } }

This works for all standard map implementations. However, be aware that setValue() is not supported by all custom Map.Entry implementations. If you are using a custom map that returns immutable entries, calling setValue() will throw UnsupportedOperationException. Always check the documentation of your map implementation if you rely on this behavior.

entrySet() vs keySet() vs values()

Choosing the right view depends on what you need to access. Here's a quick comparison:

ViewReturnsUse case
entrySet()Set<Map.Entry<K,V>>Need both keys and values, or need to modify values
keySet()Set<K>Only keys are needed, or you need to remove entries by key
values()Collection<V>Only values are needed, no key access

Using keySet() and then calling map.get(key) for each key is common but less efficient. It performs a separate hash lookup for every key, which adds overhead. entrySet() avoids that by providing direct access to the value. If you only need keys, keySet() is more concise and avoids creating Map.Entry objects (though the difference is often negligible).

Performance and Memory Considerations

The primary performance advantage of entrySet() is avoiding the extra get() call. In a loop over a large map, that can be significant. For example, with HashMap, get() has O(1) average complexity, but the constant factor is non-trivial. entrySet() iteration directly accesses the internal node's value, so it's faster.

Memory-wise, entrySet() does not create a new collection. It returns a view backed by the map. The Map.Entry objects are typically the same nodes stored in the map (for HashMap, each node implements Map.Entry). So iterating with entrySet() does not allocate extra objects. This is a key advantage over other approaches that copy data.

However, be careful with maps that do not store entries as objects, like IdentityHashMap or custom implementations. The view may create a new Map.Entry per iteration, but that's rare. In practice, for HashMap and TreeMap, entrySet() is both time and memory efficient.

Concurrency and Thread-Safety

entrySet() itself is not thread-safe. If the map is modified while another thread is iterating over entrySet(), the iterator will throw ConcurrentModificationException (fail-fast behavior). This is true for most non-concurrent map implementations.

For concurrent access, use ConcurrentHashMap. Its entrySet() iterator is weakly consistent: it reflects the state of the map at some point during iteration, but does not throw ConcurrentModificationException if the map is modified concurrently. This allows safe iteration without external synchronization, though you may see stale or partial updates.

If you need to modify the map while iterating in a multi-threaded context, consider using ConcurrentHashMap and its compute or merge methods, which provide atomic updates. Alternatively, synchronize on the map externally, but that can hurt scalability.

When to Avoid entrySet() (or Use It Carefully)

There are a few situations where entrySet() might not be the best choice:

  • You only need keys: Use keySet() to avoid the overhead of accessing Map.Entry objects, even if the overhead is small.
  • You only need values: Use values() for clarity and simplicity.
  • You need to modify the map structure (add or remove entries): While you can remove via Iterator.remove(), adding entries during iteration will cause a ConcurrentModificationException in most maps. If you need to add entries, consider collecting them and adding after the loop, or use ConcurrentHashMap's putIfAbsent or computeIfAbsent within a weakly consistent iterator.
  • You are working with a custom map that returns immutable entries: If setValue() is not supported, you'll need to use map.put() instead, which may be less efficient.

Another edge case: when you need to iterate over a map and also sort or filter entries, entrySet() gives you a Set, which does not preserve order unless the map itself is ordered (like TreeMap or LinkedHashMap). If you need to process entries in a specific order, consider converting to a list and sorting, or use a Stream with map.entrySet().stream(). The Stream API provides functional operations like filter, map, and collect that are often cleaner than manual iteration.

Map<String, Integer> topScores = scores.entrySet().stream() .filter(e -> e.getValue() >= 90) .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

This pattern is concise and leverages the power of streams, but be aware that Collectors.toMap() will throw IllegalStateException on duplicate keys. Use Collectors.toMap(..., (v1, v2) -> v1) to handle duplicates if needed.

In production, the choice between entrySet() and other views often comes down to readability and the specific operation you need. For most iteration tasks that require both key and value, entrySet() is the idiomatic and efficient choice.

java map entryset: Practical Usage and Code Examples | RYUSLOG DEV