Artículo

Java forEach: Cómo Usarlo y Todas sus Opciones (con Ejemplos)

Aprende a usar forEach en Java con expresiones lambda y method references. Diferencias con for clásico e Iterator, y errores comunes a evitar.

El método forEach en Java permite iterar sobre los elementos de una colección o un Stream de forma declarativa, usando una expresión lambda en lugar de escribir un bucle for tradicional.

¿Qué es el método forEach en Java?

forEach es un método por defecto (default) definido en la interfaz Iterable, y también existe como operación terminal en la API de Streams. En ambos casos recibe un Consumer, es decir, una función que se ejecuta para cada elemento sin devolver ningún valor.

forEach en Collections vs forEach en Streams

List<String> nombres = List.of("Ana", "Luis", "Marta");

// forEach directo sobre la colección (Iterable)
nombres.forEach(nombre -> System.out.println(nombre));

// forEach como operación terminal de un Stream
nombres.stream()
    .map(String::toUpperCase)
    .forEach(System.out::println);

La diferencia es que el forEach de Iterable recorre la colección tal cual, mientras que el de Stream se ejecuta al final de un pipeline de operaciones intermedias.

Sintaxis básica con expresión lambda

List<Integer> numeros = List.of(1, 2, 3, 4);
numeros.forEach(n -> System.out.println("Número: " + n));

Sintaxis con method reference

Cuando el cuerpo de la lambda simplemente delega en un método existente, puedes usar una referencia a método, más concisa:

numeros.forEach(System.out::println);

forEach vs for clásico vs iterator

for clásicoIteratorforEach
Acceso al índiceNo directamenteNo
Modificar la colección durante la iteraciónCon cuidado (por índice)Sí, con remove()No es seguro
LegibilidadMediaBajaAlta
Compatible con StreamsNoNo

Cuándo NO conviene usar forEach

  • Cuando necesitas el índice del elemento (usa un for clásico con contador).
  • Cuando necesitas eliminar elementos mientras iteras (usa un Iterator explícito con remove()).
  • Cuando necesitas romper la iteración antes de tiempo (break): forEach no lo permite directamente, ya que no es un bucle en el sentido clásico.

Ejemplo práctico completo

record Producto(String nombre, double precio) {}

List<Producto> productos = List.of(
    new Producto("Teclado", 45.0),
    new Producto("Monitor", 199.0),
    new Producto("Ratón", 15.0)
);

productos.stream()
    .filter(p -> p.precio() > 20)
    .forEach(p -> System.out.println(p.nombre() + ": " + p.precio() + "€"));

Errores comunes (ConcurrentModificationException y otros)

Modificar una colección mientras se recorre con forEach lanza ConcurrentModificationException:

List<String> lista = new ArrayList<>(List.of("a", "b", "c"));

lista.forEach(item -> {
    if (item.equals("b")) {
        lista.remove(item); // ConcurrentModificationException
    }
});

La solución correcta es usar removeIf() o un Iterator explícito:

lista.removeIf(item -> item.equals("b"));

Otro error habitual es esperar poder usar break o return dentro del lambda para "cortar" la iteración: dentro de forEach, return solo sale de la lambda actual, no del bucle completo.

Preguntas frecuentes

¿Puedo usar break dentro de un forEach? No directamente. Si necesitas cortar la iteración de forma anticipada, es mejor usar un bucle for clásico o una operación de Stream como anyMatch/findFirst.

¿forEach garantiza el orden de iteración? En colecciones ordenadas como List, sí. En estructuras sin orden garantizado como HashSet, no debes asumir un orden concreto.

¿forEach es más rápido que un bucle for? El rendimiento es prácticamente equivalente en la mayoría de casos; la elección debe basarse en legibilidad, no en rendimiento.

Siguiente paso

Una vez dominado forEach, el siguiente paso natural es combinarlo con filter() y Predicate para construir pipelines de Streams más expresivos.

java foreach streams lambda

Newsletter Semanal de Java

Cada viernes recibe lo más nuevo del ecosistema Java: frameworks, herramientas y mejores prácticas.

🔒 Sin spam. Cancela cuando quieras.

Compartir artículo