The forEach() Method in Java 8

Published: June 5, 2016


Introduction

Java 8 brought many enhancements to the language, and one of the most significant additions was the forEach() method. This method, part of the Iterable interface, allows you to iterate over collections in a functional style, making the code more readable and concise.

In this tutorial, we’ll cover:

  • What the forEach() method is and how to use it.
  • How to use forEach() with lambda expressions.
  • Practical examples of using forEach() with different types of collections.

What is the forEach() Method?

The forEach() method is a default method defined in the java.lang.Iterable interface. It allows you to iterate over all elements of a collection and perform an action on each element.

Syntax:

void forEach(Consumer<? super T> action);

The method takes a Consumer functional interface as an argument. The Consumer interface represents an operation that takes a single input and returns no result. In the context of forEach(), this operation is executed on each element of the collection.


Using forEach() with Collections

Let’s start with a basic example, where we use forEach() to print each element of a list of strings.

Example 1: Using forEach() with a List

import java.util.*;

public class ForEachExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        // Using forEach() to print each name
        names.forEach(name -> System.out.println(name));
    }
}

Output:

Alice
Bob
Charlie
David

In the example above, we pass a lambda expression to forEach(), which prints each name from the list.


Using forEach() with Lambda Expressions

Lambda expressions make it easier to work with functional interfaces. Instead of writing an anonymous class, you can simply pass a concise lambda expression as the argument to the forEach() method.

Example 2: Using Lambda Expression with forEach()

import java.util.*;

public class ForEachLambdaExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Using forEach() with a lambda expression to print each number
        numbers.forEach(number -> {
            int square = number * number;
            System.out.println("Square of " + number + " is: " + square);
        });
    }
}

Output:

Square of 1 is: 1
Square of 2 is: 4
Square of 3 is: 9
Square of 4 is: 16
Square of 5 is: 25

In this example, we use forEach() to square each number in the list and print the result.


Using forEach() with Other Collections

The forEach() method can be used with any class that implements the Iterable interface, such as ListSet, and Queue.

Example 3: Using forEach() with a Set

import java.util.*;

public class ForEachSetExample {
    public static void main(String[] args) {
        Set<String> uniqueNames = new HashSet<>(Arrays.asList("Alice", "Bob", "Charlie"));

        // Using forEach() with a Set
        uniqueNames.forEach(name -> System.out.println("Hello, " + name + "!"));
    }
}

Output:

Hello, Charlie!
Hello, Alice!
Hello, Bob!

Even though sets don’t allow duplicates, forEach() still works in the same way as with lists.


Combining forEach() with Method References

Java 8 introduced method references, which provide a shorthand way of invoking methods. You can use method references with forEach() to make the code even more concise.

Example 4: Using Method References with forEach()

import java.util.*;

public class ForEachMethodReferenceExample {
    public static void main(String[] args) {
        List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David");

        // Using a method reference with forEach()
        names.forEach(System.out::println);
    }
}

Output:

Alice
Bob
Charlie
David

In this example, we use the method reference System.out::println instead of the lambda expression name -> System.out.println(name).


Parallel Streams with forEach()

In addition to regular forEach(), Java 8 also allows you to use parallel streams, which can perform operations concurrently on different parts of the collection.

Example 5: Using Parallel Streams with forEach()

import java.util.*;
import java.util.stream.*;

public class ParallelForEachExample {
    public static void main(String[] args) {
        List<Integer> numbers = Arrays.asList(1, 2, 3, 4, 5);

        // Using parallel stream with forEach()
        numbers.parallelStream().forEach(number -> System.out.println(Thread.currentThread().getName() + ": " + number));
    }
}

Output (might vary):

main: 1
main: 2
main: 3
main: 4
main: 5

In this example, parallelStream() is used to process elements of the list in parallel, potentially utilizing multiple threads to perform the operation concurrently.


Best Practices

  1. Avoid Side Effects: When using forEach(), try to avoid side effects (i.e., modifying shared variables) as it can lead to unpredictable behavior, especially in parallel streams.
  2. Use forEach() with Immutable Data: It’s often best to use forEach() with immutable data structures to prevent issues related to concurrency and side effects.
  3. Prefer Streams for Complex Operations: If you need to perform more complex operations, consider using streams in combination with forEach() for better clarity and flexibility.

Conclusion

The forEach() method in Java 8 makes it easier to iterate over collections in a more declarative way. It allows for cleaner, more readable code, especially when combined with lambda expressions and method references. Whether you’re using a ListSet, or MapforEach() is a versatile tool for performing actions on collection elements.

In the next tutorial, we will explore Default Methods in Interfaces and how to use them effectively.

Comments

No comments yet. Why don’t you start the discussion?

Leave a Reply

Your email address will not be published. Required fields are marked *