forEach()
Run an action for every key and value with a lambda, and know when a plain loop or replaceAll is the better choice.
default void forEach(BiConsumer<? super K, ? super V> action)- Added in Java Java 8
Parameters
| Parameter | Type | Description |
|---|---|---|
$action | BiConsumer<? super K, ? super V> | Called with each key and its value. |
Return value
voidNothing. The action is called once for each entry, in the map's iteration order.
Errors
NullPointerException if the action is null; ConcurrentModificationException if the map is structurally modified during the call (HashMap, LinkedHashMap, TreeMap); any runtime exception thrown by the action propagates to the caller.
forEach takes a BiConsumer and calls it with every key and value: headers.forEach((name, value) -> ...). It is the shortest way to visit a map when the body is a single statement, such as logging or copying entries somewhere else. It was added to Map in Java 8, and entries are visited in the map's iteration order.
What a lambda cannot do
- Stop early. There is no
break. Areturninside the lambda only ends the call for the current entry, and the next entry runs anyway. If you want the first match, use a loop overentrySet()or a stream withfilterandfindFirst. - Throw checked exceptions.
BiConsumer.acceptdeclares none, so writing to aWriterorOutputStreamthat throwsIOExceptiondoes not compile without a try/catch inside the lambda. A plain loop lets the method declarethrows IOExceptionand keeps the real error. - Assign local variables. Locals used in a lambda must be effectively final, so a running total needs a loop, an
AtomicInteger, or better, a stream withsum().
Changing the map
Adding or removing entries from inside the action throws ConcurrentModificationException on the common map types. To change every value, use replaceAll((k, v) -> newValue), also added in Java 8. To remove matching entries, use entrySet().removeIf(...).
ConcurrentHashMap
On a ConcurrentHashMap, forEach is weakly consistent: it never throws ConcurrentModificationException, and it may or may not see updates made by other threads while it runs. That is fine for a metrics snapshot, but the result is not a consistent point-in-time view. ConcurrentHashMap also has a forEach(parallelismThreshold, action) overload that can split the work across threads.
forEach or a loop?
Both visit each entry once. Pick forEach for short side effects, a loop when you need break, checked exceptions or local state. Do not use forEach to build another collection; a stream with a collector says what you mean and handles the details.
Example
When to use it
- Copying a map of default headers onto every outgoing HTTP response.
- Logging each configuration key and value at startup, with secrets masked.
- Exporting per-endpoint counters from a ConcurrentHashMap to a metrics system.
Frequently asked questions
How do I break out of Map.forEach?
You cannot. return inside the lambda only skips the current entry. Use a for loop over entrySet() with break, or stream the entries and use filter and findFirst.
Can I modify a map inside forEach?
Not its structure: adding or removing keys throws ConcurrentModificationException on HashMap, LinkedHashMap and TreeMap. Use replaceAll to change values and entrySet().removeIf to remove entries.
Is Map.forEach faster than a for loop over entrySet?
Not in any way you should design around; both visit each entry once. Choose by readability and by whether you need break, checked exceptions or local variables.
More examples
Cleaning every value with replaceAll
replaceAll changes values in place without touching the map's structure, so it is safe where calling put inside forEach would not be. The brackets make the stripped spaces visible.
void main() {
Map<String, String> form = new LinkedHashMap<>();
form.put("email", " [email protected] ");
form.put("name", " Ada Lovelace");
form.replaceAll((field, value) -> field.equals("email")
? value.strip().toLowerCase()
: value.strip());
form.forEach((field, value) -> IO.println(field + "=[" + value + "]"));
}
// Output:
// email=[[email protected]]
// name=[Ada Lovelace]Checked exceptions need a plain loop
The loop lets writeHeaders declare IOException and pass a real write failure to its caller. Inside forEach you would have to catch it and wrap it in an unchecked exception.
void writeHeaders(Map<String, String> headers, Writer out) throws IOException {
// headers.forEach((k, v) -> out.write(k + ": " + v)); does not compile:
// BiConsumer.accept cannot throw IOException.
for (Map.Entry<String, String> e : headers.entrySet()) {
out.write(e.getKey() + ": " + e.getValue() + "\n");
}
}
void main() throws IOException {
Map<String, String> headers = new LinkedHashMap<>();
headers.put("Content-Type", "text/plain");
headers.put("Content-Length", "11");
StringWriter out = new StringWriter();
writeHeaders(headers, out);
IO.print(out);
}
// Output:
// Content-Type: text/plain
// Content-Length: 11forEach cannot stop early
forEach visited all four services even though the answer was known at the third. The loop stops as soon as it finds the slow one, and its counter is an ordinary local variable.
void main() {
Map<String, Integer> latencyMs = new LinkedHashMap<>();
latencyMs.put("db", 12);
latencyMs.put("cache", 2);
latencyMs.put("payments", 950);
latencyMs.put("search", 40);
AtomicInteger calls = new AtomicInteger();
latencyMs.forEach((service, ms) -> {
calls.incrementAndGet();
if (ms < 500) {
return; // skips this entry only
}
IO.println("slow: " + service);
});
IO.println("forEach calls: " + calls.get());
int checked = 0;
for (var e : latencyMs.entrySet()) {
checked++;
if (e.getValue() >= 500) {
IO.println("first slow: " + e.getKey() + " after " + checked + " checks");
break;
}
}
}
// Output:
// slow: payments
// forEach calls: 4
// first slow: payments after 3 checks
Discussion