Back to Blog
Java

Java HashMap forEach: Iterate Entries, Keys, and Values

java hashmap foreach: Learn how to use Java HashMap forEach to iterate entries, keys, and values with lambdas, and compare it with traditional loops.

HashMapJava 8forEachiterationlambdaentrySet
Diagram showing Java HashMap iteration with forEach method over entries, keys, and values.

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

Java's HashMap does not implement Iterable, but it provides a forEach method that accepts a BiConsumer. This method, added in Java 8, lets you iterate over each key-value pair without explicitly writing an entrySet loop. It is the most direct way to apply an action to every mapping when you need both the key and the value.

The forEach Method on HashMap

The forEach method on HashMap is defined in the Map interface. It takes a BiConsumer<? super K, ? super V> and applies that consumer to each key-value pair in the map. The signature looks like this:

void forEach(BiConsumer<? super K, ? super V> action)

A minimal example using a lambda:

Map<String, Integer> scores = new HashMap<>(); scores.put("Alice", 90); scores.put("Bob", 85); scores.put("Carol", 92); scores.forEach((name, score) -> System.out.println(name + ": " + score));

The lambda receives the key as the first parameter and the value as the second. This is simpler than manually extracting Map.Entry objects, and it reads naturally when the action is a single expression.

Iterating Entries, Keys, and Values

forEach works on the whole map, but you can also iterate over the collection views keySet(), values(), and entrySet() using their own forEach methods. Each view inherits forEach from Iterable, so the pattern is consistent.

Iterate keys:

scores.keySet().forEach(name -> System.out.println(name));

Iterate values:

scores.values().forEach(score -> System.out.println(score));

Iterate entries:

scores.entrySet().forEach(entry -> { String name = entry.getKey(); Integer score = entry.getValue(); System.out.println(name + " has " + score); });

When you need both key and value, the map-level forEach is more concise. The entrySet() variant is useful when you also need to remove entries during iteration, because Entry objects support setValue and the iterator's remove method.

Comparing forEach with entrySet() Loops

Before Java 8, the standard way to iterate a map was a for-each loop over entrySet():

for (Map.Entry<String, Integer> entry : scores.entrySet()) { System.out.println(entry.getKey() + ": " + entry.getValue()); }

The forEach version removes the explicit Map.Entry type and the loop boilerplate. For simple actions, it is shorter and often clearer. However, the traditional loop gives you more control over the iteration flow. You can use break, return, or continue directly inside the loop. With forEach, you are inside a lambda, so a return exits the lambda, not the entire method. To stop early, you must throw an exception or use a different iteration approach.

// This prints only the first entry and then stops the whole method scores.forEach((name, score) -> { System.out.println(name); return; // exits the lambda, not the forEach });

If you need conditional termination, a traditional loop is more straightforward.

Performance and Ordering Behavior

HashMap does not guarantee any iteration order. The order can change when entries are added or removed, and it may differ between JVM runs. forEach inherits this behavior because it delegates to the map's internal entry iterator. If your code relies on a stable order, use LinkedHashMap or TreeMap instead.

In terms of runtime cost, forEach is not inherently faster than a for-each loop. The lambda may introduce a small allocation overhead if it captures variables, but the JIT compiler often inlines simple lambdas. For most applications, the difference is negligible. The real cost is the same as any full map traversal: O(n) time and no extra space beyond the iterator itself.

When you need to iterate frequently and performance is critical, measure with a profiler rather than assuming one syntax is faster. The choice between forEach and a loop rarely becomes a bottleneck.

Handling Concurrent Modification

HashMap is not thread-safe. If one thread modifies the map structurally while another thread iterates it, the iteration may throw ConcurrentModificationException. This applies to both forEach and traditional loops because both use fail-fast iterators.

Map<String, Integer> map = new HashMap<>(); map.put("a", 1); map.put("b", 2); // ConcurrentModificationException if another thread adds an entry map.forEach((k, v) -> System.out.println(k));

If you need to iterate and modify from multiple threads, use ConcurrentHashMap. Its iterators are weakly consistent: they do not throw ConcurrentModificationException and reflect the state of the map at some point during iteration. ConcurrentHashMap also provides its own forEach overloads that accept a parallelism threshold, which can process entries in parallel using the common ForkJoin pool.

ConcurrentHashMap<String, Integer> concurrent = new ConcurrentHashMap<>(); concurrent.put("x", 1); concurrent.forEach(1, (k, v) -> System.out.println(k)); // parallel threshold 1

For single-threaded code, the standard HashMap.forEach is safe as long as you do not modify the map inside the lambda. Modifying the map from within the same iteration is also problematic: adding or removing entries during forEach will throw ConcurrentModificationException because the iterator checks the modCount.

Null Keys and Values in forEach

HashMap permits one null key and any number of null values. The forEach method handles these without special logic. The BiConsumer receives the null key or null value as an ordinary argument.

Map<String, String> map = new HashMap<>(); map.put(null, "null key"); map.put("key", null); map.forEach((k, v) -> System.out.println(k + " -> " + v));

This prints null -> null key and key -> null. If your action performs operations that assume non-null values, you must add explicit null checks inside the lambda. The iteration itself does not skip null entries.

Choosing the Right Iteration Approach

The decision between forEach and a traditional loop depends on what you need to do inside the iteration.

Use forEach when:

  • The action is a simple, self-contained operation on each key-value pair.
  • You do not need to break out of the loop early.
  • You prefer a more functional style with lambdas.

Use a traditional for-each loop over entrySet() when:

  • You need to break or return from the enclosing method based on a condition.
  • You need to remove the current entry using the iterator's remove method.
  • You are working with code that predates Java 8 and you want to keep the style consistent.

For iterating only keys or only values, the collection views with forEach are straightforward. If you need to modify values in place, use entrySet() with setValue; the map-level forEach does not give you a reference to the Entry object.

scores.entrySet().forEach(entry -> entry.setValue(entry.getValue() + 1));

This increments every value without replacing the map. The map-level forEach cannot do that because it only provides the key and value, not the entry.

Understanding these tradeoffs lets you pick the iteration style that matches the control flow and mutability requirements of your code.

java hashmap foreach: Practical Usage and Code Examples | RYUSLOG DEV