Introduction
Java 8 introduced the Stream API, which allows for functional-style operations on collections of data. One of the most common operations you might need to perform on a stream is iterating over its elements to perform some action. The forEach method in the Stream API is designed for this purpose. It allows you to iterate over each element in the stream and apply a specified action, typically represented as a lambda expression.
In this guide, we’ll explore how to use the forEach method in Java 8 streams, including its common use cases and best practices.
Table of Contents
- Problem Statement
- Solution Steps
- Java Program
- Example 1: Basic Usage of forEach
- Example 2: Using forEachwith Method References
- Example 3: Modifying Elements with forEach
- Example 4: forEachvsforEachOrdered
- Example 5: Side Effects and Considerations
 
- Example 1: Basic Usage of 
- Conclusion
Problem Statement
When working with collections, you often need to perform operations on each element, such as printing values, accumulating results, or modifying data. The goal is to use the forEach method in the Stream API to handle these operations in a concise and readable way.
Example:
- Problem: Iterating over elements in a collection and performing actions like printing or modifying each element.
- Goal: Use the Stream API’s forEachmethod to apply actions to each element in a stream efficiently and cleanly.
Solution Steps
- Understand Basic Usage: Learn how to use forEachto iterate over elements in a stream.
- Use Method References: Simplify lambda expressions with method references where applicable.
- Modify Elements: Explore how to modify elements inside forEach.
- Use forEachOrdered: Understand when to useforEachOrderedinstead offorEach.
- Consider Side Effects: Be aware of potential side effects when using forEach.
Java Program
Example 1: Basic Usage of forEach
The simplest use case for forEach is iterating over each element in a stream and performing an action, such as printing the elements.
import java.util.Arrays;
import java.util.List;
/**
 * Java 8 - Basic Usage of forEach
 * Author: https://www.rameshfadatare.com/
 */
public class ForEachExample1 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
        // Using forEach to print each name
        names.stream().forEach(name -> System.out.println(name));
    }
}
Output
John
Alice
Bob
Charlie
Explanation
- forEach(name -> System.out.println(name)): Iterates over each element in the stream and prints it. The lambda expression- name -> System.out.println(name)defines the action to be performed on each element.
Example 2: Using forEach with Method References
Java 8 allows you to replace certain lambda expressions with method references, which can make the code more concise.
import java.util.Arrays;
import java.util.List;
/**
 * Java 8 - Using forEach with Method References
 * Author: https://www.rameshfadatare.com/
 */
public class ForEachExample2 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
        // Using forEach with method reference to print each name
        names.stream().forEach(System.out::println);
    }
}
Output
John
Alice
Bob
Charlie
Explanation
- System.out::println: This method reference is equivalent to the lambda expression- name -> System.out.println(name)and makes the code more concise.
Example 3: Modifying Elements with forEach
While forEach is primarily used for performing actions like printing, you can also modify the state of objects if needed.
import java.util.Arrays;
import java.util.List;
/**
 * Java 8 - Modifying Elements with forEach
 * Author: https://www.rameshfadatare.com/
 */
public class ForEachExample3 {
    public static void main(String[] args) {
        List<Person> people = Arrays.asList(
            new Person("John", 30),
            new Person("Alice", 25),
            new Person("Bob", 28)
        );
        // Using forEach to increase the age of each person by 1
        people.stream().forEach(person -> person.setAge(person.getAge() + 1));
        // Print the updated list
        people.forEach(person -> System.out.println(person.getName() + ": " + person.getAge()));
    }
}
class Person {
    private String name;
    private int age;
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    public String getName() {
        return name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
Output
John: 31
Alice: 26
Bob: 29
Explanation
- person -> person.setAge(person.getAge() + 1): The- forEachmethod is used to modify the- ageproperty of each- Personobject by incrementing it by 1.
Example 4: forEach vs forEachOrdered
The forEach method does not guarantee the order of execution in parallel streams, but forEachOrdered does.
import java.util.Arrays;
import java.util.List;
/**
 * Java 8 - forEach vs forEachOrdered
 * Author: https://www.rameshfadatare.com/
 */
public class ForEachExample4 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
        // Using forEach with a parallel stream (order not guaranteed)
        System.out.println("Using forEach:");
        names.parallelStream().forEach(System.out::println);
        // Using forEachOrdered with a parallel stream (order guaranteed)
        System.out.println("Using forEachOrdered:");
        names.parallelStream().forEachOrdered(System.out::println);
    }
}
Output (example, actual output may vary)
Using forEach:
Alice
John
Charlie
Bob
Using forEachOrdered:
John
Alice
Bob
Charlie
Explanation
- forEach(): In a parallel stream,- forEachdoes not guarantee the order of processing.
- forEachOrdered(): Ensures that elements are processed in the order of the source, even in parallel streams.
Example 5: Side Effects and Considerations
While forEach is powerful, it’s important to avoid unintended side effects when modifying external state.
import java.util.Arrays;
import java.util.List;
import java.util.concurrent.atomic.AtomicInteger;
/**
 * Java 8 - Side Effects in forEach
 * Author: https://www.rameshfadatare.com/
 */
public class ForEachExample5 {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("John", "Alice", "Bob", "Charlie");
        // Avoid side effects by using a safe approach
        AtomicInteger counter = new AtomicInteger(0);
        names.stream().forEach(name -> {
            int count = counter.incrementAndGet();
            System.out.println("Processing " + name + ": Count " + count);
        });
    }
}
Output
Processing John: Count 1
Processing Alice: Count 2
Processing Bob: Count 3
Processing Charlie: Count 4
Explanation
- Avoiding Side Effects: Use thread-safe constructs like AtomicIntegerwhen you need to modify external state inforEach, especially in parallel streams.
Conclusion
The forEach method in Java 8’s Stream API is used for iterating over elements in a collection and applying actions to them. Whether you’re printing values, modifying data, or working with parallel streams, forEach provides a concise and readable way to process elements. However, it’s important to be mindful of potential side effects, especially when modifying shared state. By understanding how to use forEach effectively, you can write cleaner and more efficient code in Java 8.