Java 8 forEach()

In this article, we will understand forEach() loop added in java 8 to iterate over Collections such as a list, set or map or over java streams with example programs.

Java forEach() syntax

forEach() is a method introduced in java 8 in java.lang.Iterable interface and can be used to iterate over a collection.

forEach() is a default interface method which takes a java.util.function.Consumer as argument.
Syntax of forEach() method is given below

void forEach(Consumer<? super T> action) { }

Java docs for this method state,

Performs the given action for each element of the Iterable until all elements have been processed or the action throws an exception.

Using forEach()

forEach() accepts an argument of type Consumer. It is a functional interface and hence, contains a single method accept(), which takes a single argument and returns no value.

Signature of accept() method is

void accept(T t);

forEach() method is invoked on a collection and is invoked automatically for each element of the collection.
Thus, it can be said that for every element, accept() method of Consumer interface is invoked with a collection element as argument value.
We need to define implementation of accept() method as per our requirement.

There are 3 ways to implement accept() method.

1. Implement Consumer interface in a class

Create a class implementing Consumer interface and hence, its accept() method as shown below.

public class ConsumerImpl implements Consumer<String> {
   @Override
   public void accept(String s) {
      System.out.println(s);
   }
}

Then, supply the object of this class to forEach() as below

list.forEach(new ConsumerImpl());

2. Anonymous Consumer Implementation

Instead of creating a separate class for implementing Consumer interface, define implementation of accept() method inline as an anonymous inner class as shown below

list.forEach(new Consumer<String>(){
   public void accept(String t) {
      System.out.println(t);
   }
});
Consumer is a generic interface.
Type of Consumer and the type of argument of accept() method must be same, which is, String in above example.

3. As a Lambda expression

Since Consumer is a Functional interface, its accept() method can be implemented as a Lambda expression as shown below

list.forEach((t) -> {
   System.out.println(t);
});

In all examples of forEach(), we will be using lambda expression, since it makes the code concise and clean. Also, this is the most widely implemented usage of forEach().

Java forEach() over list

A list can be iterated using forEach() as shown in the example below.

// define a list of integers
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// iterate with forEach
numbers.forEach(num -> {
      System.out.println(num);
});

Java forEach() over ArrayList

More often, we need to iterate over an arraylist of custom or user defined objects.
With forEach(), this can be done as in below example

class Employee {
  private int id;
  private String name;

  public Employee(int id, String name) {
    this.id = id;
    this.name = name;
  }

  // getter and setter methods
}

public class ArrayListIterator {
  public static void main(String ... a) {
    // create a list
    List<Employee> emp = new ArrayList<>();
    // create employee objects
    Employee one = new Employee(1, "A");
    Employee two = new Employee(2, "B");
    // add objects to list
    emp.add(one);
    emp.add(two);
    // iterate list of objects with forEach
    emp.forEach(e -> {
      System.out.println("Employee name: " + e.getName());
    });
}

Here, we have an arraylist of objects of custom class, Employee, which is iterated using a forEach() loop.

Java forEach() stream

Java 8 streams can also be iterated using forEach() method. Example program is given below.

// define a list of integers
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
// iterate over a stream
numbers.stream().forEach(
   num -> System.out.println(num)
);

Java forEach() over Map

forEach() method can be used to loop over a Map as shown below. java.util.Map interface defines a different forEach() method which takes a BiConsumer as argument.

java.util.function.BiConsumer is a Functional interface with a single method accept() that takes two arguments as shown below.

void accept(T t, U u);

When forEach() is invoked on a map, it internally invokes accept() method with key and value of current map element passed as arguments. Example,

Map<Integer, String> map = new HashMap<>();
map.put(1, "A");
map.put(2, "B");
map.put(3, "C");
map.forEach((k,v) -> {
    System.out.println("Key: " + k + ", Value: " + v);
});

This prints

 

Key: 1, Value: A
Key: 2, Value: B
Key: 3, Value: C

Java forEach() over array

To use forEach() over a java array, we need to get a stream over an array using Arrays.stream() method as shown below.

int[] nums = {1, 2, 3, 4, 5};
Arrays.stream(nums).forEach(num-> System.out.print(num));

forEach() cannot be directly used over an array, since it is defined in Iterable interface and arrays are no way related to this interface.

Java forEach() index

Java forEach() loop has no mechanism to access loop counter or element index directly as in traditional for loop, since you don’t need it to access elements.
But, in case, it is required to iterate a list with index, then you can initialize an integer to 0 before the loop and increment it inside the loop as shown below

// define a list of integers 
List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10); 
// initialize index variable
int counter = 0;
numbers.forEach( num -> {
  System.out.println(num); 
  counter++;
});

forEach() vs for-each

for-each, also known as enhanced for loop is compared with forEach() but both of these have differences. Following are the important differences summarized below.

1. forEach() is a method while for-each is a loop.
2. forEach() is introduced in java 1.8 while for-each loop was added in java 1.5.
3. You can supply lambda expresssion to forEach() but for-each loop doesn’t support lambda expressions.
4. Lambda expression used in forEach() loop cannot use non-final variables. Thus, below code is not allowed

int sum = 0;
list.forEach( x -> {
 // error: sum is not final
 sum += x;
});

While it is allowed in for-each loop.
5. You cannot use break statement in a lambda expression given in forEach() while it can be used in a for-each loop.

Conclusion

In conclusion, this article delved into the versatility of the Java 8 forEach() loop for iterating through various data structures like lists, maps, streams, and arrays.
Beginning with an overview of the forEach() syntax, it explored its usage and benefits.
Comparison between forEach() and traditional for-each loops shed light on their differences and when to choose each approach.
By mastering forEach() loop, Java developers can streamline their code, improve readability, and leverage the enhanced functionality offered by Java 8.

Hope the article was useful.