You know that clunky feeling of writing yet another for loop just to print out a list. Counter variable, condition check, increment — three moving parts before you even get to the part you actually care about. Java 8 gave you a way around that: forEach.
It sits inside the Iterable interface and works hand-in-hand with the Stream API. You hand it an action, and it runs that action on every element in your collection. No index tracking. No boilerplate.
Here's the method signature you're working with:
void forEach(Consumer<? super T> action)
action is whatever you want done to each element. Consumer comes from java.util.function, and its whole job is simple: take one input, do something with it, hand back nothing. That's a perfect fit for "run this on every item in my list."
Let's walk through how it actually plays out in code.
Printing a List, the Easy Way
import java.util.ArrayList;
public class ForEachExample {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// Using forEach to print each name
names.forEach(name -> System.out.println(name));
}
}
That lambda is doing all the work. name -> System.out.println(name) tells Java: for every name in this list, print it. You're not managing where you are in the list — Java handles that part.
Skip the Lambda, Use a Method Reference
import java.util.ArrayList;
public class ForEachExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// Using forEach with method reference to print each number
numbers.forEach(System.out::println);
}
}
Why type out a lambda that just calls one method? System.out::println gets you the exact same result with fewer keystrokes. This is your instinct once you get comfortable with forEach — reach for the method reference whenever the lambda body is just a single call.
Transforming Values on the Way Out
import java.util.ArrayList;
public class ForEachExample {
public static void main(String[] args) {
ArrayList<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
// Using forEach to double each number
numbers.forEach(num -> System.out.println(num * 2));
}
}
You're not stuck printing values exactly as they are. Here, every number gets doubled before it shows up on the console. Swap out the math, and you can run any calculation you need — the action is entirely up to you.
Reusing the Same Logic Across Multiple Lists
import java.util.ArrayList;
import java.util.function.Consumer;
public class ForEachExample {
public static void main(String[] args) {
ArrayList<String> names = new ArrayList<>();
names.add("Alice");
names.add("Bob");
names.add("Charlie");
// Custom functional interface for printing names
Consumer<String> printName = name -> System.out.println("Name: " + name);
// Using forEach with custom functional interface
names.forEach(printName);
}
}
This pattern pays off the moment you need the same action in more than one place. Build your Consumer once, save it to a variable, and pass it into forEach on any list that matches its type. Write it once, use it everywhere.
Look back at all four examples and you'll notice the same shape repeating. You're never managing an index. You're never writing loop scaffolding. You're just telling Java what should happen to each element, and it takes care of the rest. Less code fighting for your attention, more room to focus on what your program actually does.