Menu

Bucle for-each en Java: sintaxis, ejemplos y trampas

El bucle for-each de Java (for mejorado) explicado: iteración limpia sobre arrays y colecciones, cuándo usarlo y la trampa de la modificación que pilla a todo el mundo.

Esta página incluye editores ejecutables: edita, ejecuta y ve el resultado al instante.

Cuando el índice solo estorba

Un bucle for con contador te da un contador, una condición y un paso de actualización. Pero gran parte del tiempo en realidad no te importa la posición de un elemento: solo quieres hacer algo con cada uno, en orden, de principio a fin. Gestionar un índice para eso es trabajo innecesario, y es justo donde se cuelan los errores de desplazamiento por uno (off-by-one).

El bucle for-each (Java lo llama for mejorado) elimina el contador por completo. Nombras una variable, la apuntas a una colección y el bucle te entrega cada elemento por turnos.

Sintaxis básica

La forma es for (Tipo elemento : coleccion). Lee los dos puntos como la palabra "en":

No hay i, ni scores.length, ni scores[i]. En cada pasada, score es el siguiente elemento. El bucle se ejecuta una vez por elemento y se detiene automáticamente cuando no quedan más: no puedes pasarte del final ni empezar un elemento demasiado pronto.

Recorrer una lista

El mismo bucle funciona con cualquier cosa iterable, lo que incluye List, Set y los demás tipos de colección. El tipo del elemento va antes del nombre de la variable:

Fíjate en que no tuviste que saber ni preocuparte de si langs se respalda con un array, una lista enlazada o cualquier otra cosa: for-each funciona igual en todas ellas. Esa es su verdadera fortaleza: una sintaxis legible para cada colección.

var te ahorra el nombre del tipo

Si el tipo del elemento es largo u obvio, var deja que el compilador lo infiera para que no te repitas:

Sin var, esa variable del bucle sería el trabalenguas Map.Entry<String, Integer>. var lo mantiene legible, y el tipo sigue estando totalmente comprobado en tiempo de compilación: no es un tipo dinámico ni laxo.

La trampa de la modificación

Aquí está la regla que pilla a todo el mundo: no puedes añadir ni eliminar elementos de una colección mientras un bucle for-each la recorre. Hacerlo lanza una ConcurrentModificationException:

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

for (String item : items) {
    if (item.equals("b")) {
        items.remove(item);   // lanza ConcurrentModificationException
    }
}

El bucle nota que la lista cambió por debajo y se detiene en lugar de saltarse o repetir elementos en silencio. Para eliminar de forma segura, baja a un Iterator explícito, que tiene un remove() que el bucle conoce:

Un atajo habitual es items.removeIf(item -> item.equals("b")), que hace lo mismo en una sola línea.

Solo lectura, sin reasignar

Otra limitación sutil: asignar a la variable del bucle cambia únicamente la copia local, no la colección. Esto sorprende a quien viene de lenguajes donde la variable del bucle es una referencia viva:

Si necesitas escribir de vuelta en el array, necesitas el índice, y eso significa el clásico bucle for con contador: for (int i = 0; i < nums.length; i++) nums[i] = nums[i] * 10;. Para elementos que son objetos, puedes mutar el objeto al que apunta la variable (llamando a un setter, por ejemplo), pero no puedes reemplazarlo dentro de la colección.

break y continue siguen funcionando

Un for-each es un bucle de verdad, así que break y continue se comportan exactamente igual que en cualquier otro sitio: break sale del bucle y continue salta al siguiente elemento:

Esto imprime keep y luego keep: salta "skip" y se detiene en "stop" antes de llegar a "never". Así que no estás obligado a visitar todos los elementos; simplemente renuncias al índice a cambio de un código más limpio.

Siguiente: Arrays

Ya has recorrido arrays varias veces sin detenerte a pensar en qué son realmente: contenedores indexados de tamaño fijo con ese campo .length. La siguiente página vuelve al principio y cubre los arrays como es debido: cómo declararlos, los valores por defecto con los que empiezan y cómo su tamaño fijo se diferencia de un ArrayList que puede crecer.

Preguntas frecuentes

¿Qué es un bucle for-each en Java?

Un bucle for-each (también llamado for mejorado) recorre todos los elementos de un array o una colección sin necesidad de un contador: for (Tipo elemento : coleccion) { ... }. Se lee como "para cada elemento de la colección". Es más limpio que un bucle for con contador cuando solo necesitas cada elemento y nunca el índice.

¿Cuál es la diferencia entre un bucle for y un bucle for-each en Java?

El bucle for clásico usa un contador explícito (for (int i = 0; i < arr.length; i++)), así que controlas el índice y la dirección. El bucle for-each, for (Tipo x : arr), no tiene índice: visita todos los elementos en orden. Usa for-each para recorridos de solo lectura de principio a fin; usa el bucle con contador cuando necesites el índice, quieras saltarte elementos o modificar la estructura de la colección.

¿Por qué mi bucle for-each lanza ConcurrentModificationException?

Porque llamaste a add() o remove() sobre la colección mientras la iterabas con un for-each. El bucle detecta el cambio estructural y lanza la excepción para protegerte de un comportamiento indefinido. Para eliminar elementos de forma segura, usa un Iterator explícito y su método remove(), o recopila los elementos a borrar y elimínalos después del bucle.

Coddy programming languages illustration

Aprende a programar con Coddy

COMENZAR