forEach()
The forEach() method is a terminal operation used to iterate over elements and perform an action on each element. It exists in both the Collection interface and the Stream API, with important behavioral differences.
This is a common interview topic, especially forEach() vs forEachOrdered() and Collection vs Stream usage.
What Is forEach()?
- Executes an action for each element
- Uses internal iteration
- Accepts a Consumer functional interface
- Returns void (terminal operation in streams)
forEach() in Collections
Method Signature
void forEach(Consumer super T> action)
Example
Listlist = List.of("Java", "Python", "C++"); list.forEach(s -> System.out.println(s));
- Iterates in encounter order (for List)
- Modifies allowed (not recommended)
forEach() in Stream API
list.stream()
.forEach(s -> System.out.println(s));
- Terminal operation
- Stream is consumed
- Cannot reuse stream afterward
forEach() vs Enhanced for-loop
| Aspect | forEach() | for-each loop |
|---|---|---|
| Style | Functional | Imperative |
| Iteration | Internal | External |
| Lambda support | ✔ Yes | ❌ No |
| Break/Continue | ❌ No | ✔ Yes |
| Parallel-friendly | ✔ Yes | ❌ No |
forEach() vs map() (Very Important)
| Method | Purpose |
|---|---|
| map() | Transform elements |
| forEach() | Perform side effects |
❌ Wrong
list.stream().forEach(x -> x * 2); // no effect
✔ Correct
list.stream()
.map(x -> x * 2)
.forEach(System.out::println);
forEach() vs forEachOrdered() (Interview Favorite)
forEach()
- Order not guaranteed in parallel streams
- Faster
list.parallelStream().forEach(System.out::println);
forEachOrdered()
- Maintains encounter order
- Slightly slower
list.parallelStream().forEachOrdered(System.out::println);
Using forEach() with Maps
Best Practice (entrySet)
map.forEach((key, value) ->
System.out.println(key + " = " + value)
);
- Clean
- Efficient
Side Effects & Caution (Interview Trap)
forEach() is intended for side effects (logging, printing).
❌ Avoid modifying shared state:
list.stream().forEach(x -> sharedList.add(x)); // risky
✔ Prefer:
Listresult = list.stream().map(x -> x * 2).toList();
Exception Handling in forEach()
- Lambdas cannot throw checked exceptions directly
- Must handle inside lambda
list.forEach(x -> {
try {
// risky operation
} catch (Exception e) {
e.printStackTrace();
}
});
Common Beginner Mistakes
- Using forEach() instead of map()
- Expecting return values
- Modifying source collection
- Assuming order in parallel streams
- Overusing forEach() for business logic
Best Practices
- Use forEach() for final actions (print, log)
- Prefer map(), filter(), collect() for transformations
- Use forEachOrdered() when order matters
- Avoid shared mutable state
- Keep lambdas simple
More forEach() Examples
1. forEach() with List
import java.util.*;
class Demo {
public static void main(String[] args) {
List list = Arrays.asList("Java", "Selenium");
list.forEach(s -> System.out.println(s));
}
}
2. forEach() with Method Reference
list.forEach(System.out::println);
3. forEach() with Set
import java.util.*;
class Demo {
public static void main(String[] args) {
Set set = new HashSet<>(Arrays.asList(1, 2, 3));
set.forEach(n -> System.out.println(n));
}
}
4. forEach() with Map (BiConsumer)
import java.util.*;
class Demo {
public static void main(String[] args) {
Map map = Map.of(1, "A", 2, "B");
map.forEach((k, v) -> System.out.println(k + " = " + v));
}
}
5. forEach() on Stream
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3)
.stream()
.forEach(n -> System.out.println(n));
}
}
6. forEach() vs forEachOrdered() (Parallel Stream)
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3, 4)
.parallelStream()
.forEach(System.out::println);
}
}
Note: Order not guaranteed.
7. forEachOrdered() to Maintain Order
Arrays.asList(1, 2, 3, 4)
.parallelStream()
.forEachOrdered(System.out::println);
8. forEach() with Filtering (Stream Chain)
import java.util.*;
class Demo {
public static void main(String[] args) {
Arrays.asList(1, 2, 3, 4)
.stream()
.filter(n -> n % 2 == 0)
.forEach(System.out::println);
}
}
9. forEach() with Transformation
Arrays.asList("java", "api")
.stream()
.map(String::toUpperCase)
.forEach(System.out::println);
10. forEach() with Index (Workaround)
import java.util.*;
class Demo {
public static void main(String[] args) {
List list = Arrays.asList("A", "B", "C");
for (int i = 0; i < list.size(); i++) {
System.out.println(i + " -> " + list.get(i));
}
}
}
Interview Note: forEach() does not provide index.
11. forEach() Modifying External Variable (Trap)
int sum = 0;
// Arrays.asList(1,2,3).forEach(n -> sum += n); // ❌ compilation error
Reason: Variable must be effectively final.
12. Correct Way Using AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
import java.util.*;
class Demo {
public static void main(String[] args) {
AtomicInteger sum = new AtomicInteger();
Arrays.asList(1, 2, 3)
.forEach(n -> sum.addAndGet(n));
System.out.println(sum.get());
}
}
13. forEach() vs Enhanced for Loop
// Enhanced for
for (String s : list) {
System.out.println(s);
}
// forEach
list.forEach(System.out::println);
14. forEach() with Exception Handling
list.forEach(s -> {
try {
System.out.println(s);
} catch (Exception e) {
System.out.println("Error");
}
});
15. ❌ Removing Elements Inside forEach() (Trap)
list.forEach(s -> {
// list.remove(s); // ❌ ConcurrentModificationException
});
16. Correct Removal Using Iterator
Iteratorit = list.iterator(); while (it.hasNext()) { if (it.next().equals("A")) { it.remove(); } }
17. forEach() with Custom Object
class User {
String name;
User(String name) { this.name = name; }
}
List users = List.of(new User("A"), new User("B"));
users.forEach(u -> System.out.println(u.name));
18. forEach() in Optional
import java.util.*;
class Demo {
public static void main(String[] args) {
Optional opt = Optional.of("Java");
opt.forEach(System.out::println);
}
}
19. forEach() as Terminal Operation
Arrays.asList(1, 2, 3)
.stream()
.forEach(System.out::println); // terminal
Rule: After forEach(), stream cannot be reused.
20. forEach() with Nested Collections
import java.util.*;
class Demo {
public static void main(String[] args) {
List> list =
Arrays.asList(Arrays.asList(1,2), Arrays.asList(3,4));
list.forEach(inner ->
inner.forEach(System.out::println)
);
}
}
21. forEach() with Queue
import java.util.*;
class Demo {
public static void main(String[] args) {
Queue q = new LinkedList<>(List.of("A", "B"));
q.forEach(System.out::println);
}
}
22. forEach() with CopyOnWriteArrayList
import java.util.concurrent.*;
class Demo {
public static void main(String[] args) {
CopyOnWriteArrayList list =
new CopyOnWriteArrayList<>(List.of(1,2));
list.forEach(n -> list.add(3)); // allowed
System.out.println(list);
}
}
23. forEach() vs map() (Interview Trap)
// Wrong
list.forEach(s -> s.toUpperCase());
// Correct
list.stream().map(String::toUpperCase).forEach(System.out::println);
24. forEach() with Logging Use Case
logs.stream()
.filter(l -> l.contains("ERROR"))
.forEach(System.out::println);
25. Interview Summary – forEach()
collection.forEach()
stream.forEach()
map.forEach()
Key Points
- Terminal operation (for streams)
- No index support
- Order not guaranteed in parallel streams
- Cannot modify collection structure
- Best with lambdas & method references
Interview-Ready Answers
Short Answer
forEach() performs an action on each element of a collection or stream.
Detailed Answer
In Java, forEach() is a terminal operation that accepts a Consumer and executes it for each element. In streams, it triggers execution and may not preserve order in parallel streams unless forEachOrdered() is used. It is mainly intended for side effects rather than data transformation.
Key Takeaway
forEach() is for actions, not transformations. Use it at the end of a stream pipeline, and be cautious with parallel execution and side effects.