Cómo funciona for-each en Java: análisis con ejemplos desde principiante hasta experto

Entrenador de Python en Línea para Principiantes

Aprende Python fácilmente sin sobrecargarte de teoría. Resuelve tareas prácticas con verificación automática, recibe pistas y escribe código directamente en el navegador, sin instalar nada.

Start Course

Introducción: ¿por qué for-each se convirtió en el "rey" de los bucles en Java?

\n\n

Si has escrito en Java al menos una semana, seguro que te has encontrado con la construcción for (Type var : collection). Esta sintaxis concisa se llama bucle for-each (o enhanced for loop). Apareció en Java 5 (2004) y rápidamente se convirtió en la favorita de los desarrolladores.

\n\n

Pero, ¿sabes qué sucede bajo el capó cuando la JVM encuentra esta construcción? ¿Por qué for-each puede ser más lento que el for normal para arrays? ¿Y por qué a veces lanza ConcurrentModificationException, incluso si no estás eliminando nada?

\n\n

En este artículo analizaremos cómo funciona for-each en Java a nivel de bytecode, lo compararemos con los bucles clásicos y aprenderemos a evitar las trampas típicas. ¡Allá vamos!

\n\n

Sintaxis y primer contacto

\n\n

La sintaxis básica es la siguiente:

\n\n
for (TipoElemento variable : colección_o_array) {\n    // cuerpo del bucle\n}
\n\n

Ejemplo con ArrayList:

\n\n
List<String> names = Arrays.asList("Ana", "Boris", "Víctor");\n\nfor (String name : names) {\n    System.out.println("¡Hola, " + name + "!");\n}
\n\n

Salida:

\n\n
¡Hola, Ana!\n¡Hola, Boris!\n¡Hola, Víctor!
\n\n

¿Sencillo? Sí. Pero detrás de esta simplicidad se esconden dos mecanismos completamente diferentes: uno para arrays y otro para colecciones.

\n\n

¿Cómo funciona for-each con arrays?

\n\n

Cuando escribes for-each para un array, el compilador de Java lo convierte en un for normal con índice. Sin magia.

\n\n

Tu código:

\n\n
int[] numbers = {10, 20, 30};\n\nfor (int num : numbers) {\n    System.out.println(num);\n}
\n\n

Después de la compilación (descompila el bytecode para comprobarlo):

\n\n
int[] numbers = new int[]{10, 20, 30};\nfor (int i = 0; i < numbers.length; i++) {\n    int num = numbers[i];\n    System.out.println(num);\n}
\n\n

Conclusión importante: para arrays, for-each y el for normal son lo mismo en cuanto a rendimiento. Sin gastos generales de iteradores.

\n\n

¿Cómo funciona for-each con colecciones (List, Set)?

\n\n

Aquí es donde se pone interesante. Para las colecciones, for-each utiliza un iterador.

\n\n

Tu código:

\n\n
List<String> list = new ArrayList<>();\nlist.add("Java");\nlist.add("Python");\nlist.add("C++");\n\nfor (String lang : list) {\n    System.out.println(lang);\n}
\n\n

El compilador lo transforma en:

\n\n
List<String> list = new ArrayList<>();\nlist.add("Java");\nlist.add("Python");\nlist.add("C++");\n\nfor (Iterator<String> it = list.iterator(); it.hasNext(); ) {\n    String lang = it.next();\n    System.out.println(lang);\n}
\n\n

Es decir:

\n
    \n
  • Se llama a list.iterator() — obtenemos un iterador.
  • \n
  • En la condición — it.hasNext().
  • \n
  • En el cuerpo — it.next().
  • \n
  • La variable lang se inicializa con el resultado de next().
  • \n
\n\n

¿Qué significa esto en la práctica?

\n
    \n
  • No se pueden eliminar elementos dentro de for-each mediante list.remove() — obtendrás un ConcurrentModificationException. Usa it.remove() o un for normal.
  • \n
  • No se puede modificar la colección (añadir/eliminar) durante la iteración — el mismo error.
  • \n
  • Rendimiento: para ArrayList, la diferencia con el for normal es mínima (creación del objeto iterador). Para LinkedList, for-each es mucho más rápido que for (int i = 0; i < list.size(); i++), porque get(i) en LinkedList es O(n) cada vez.
  • \n
\n\n

¿Y qué pasa con Map? Iteración sobre pares clave-valor

\n\n

Map no implementa Iterable directamente, por lo que for-each

Blogs

Book Recommendations