4 formas de hacer un bucle for en Java

Escrito por picodotdev el , actualizado el .
java planeta-codigo programacion
Enlace permanente Comentarios

La forma habitual en Java de hacer un bucle es con una sentecia for o while pero con el añadido de los iteradores en Java 5 no hace falta tener una variable para conservar el índice del bucle. Ya en Java 8 se han añadido los streams que ofrecen otras nuevas formas de iterar sobre los elementos de una colección en este último caso con técnicas propias de lenguajes funcionales.

Hasta Java 5 para hacer un bucle desde 0 a N elementos había que usar una variable para mantener un contador, hacer una comparación para comprobar si se había llegado al límite e incrementar la variable en la siguiente ejecución. El código era bastante verboso y dado que los bucles son una construcción básica de cualquier lenguaje de programación es empleada numerosas veces en cualquier algoritmo.

Algunos de estos ejemplos de bucles son utilizables a partir de Java 5, en versiones más recientes se han añadido muchas otras novedades como las lamdas, streams, metodos en interfaces y API para fechas en Java 8, la modularidad, try-with-resource mejorado, jlink o un nuevo modelo de publicación en Java 9, inferencia de tipos para variables locales en Java 10, un cliente HTTP en Java 11 y otras novedades en el lenguaje y la plataforma Java.

Los bucles son uno de los tipos de sentencias y estructuras de control de flujo básicas de Java que permiten repetir la ejecución de un bloque de sentencias mientras se cumpla la expresión de condición de repetición, en cada iteración del bucle se evalua la expresión de condición y en el momento que no se cumple se continua con la siguiente sentencia del programa.

Bucle for

Antes de Java 5 un bucle for de 0 a 5 y de una colección se realizaba de la siguiente manera manteniendo una variable normalmente de nombre i que hace de contador y j si el bucle for está anidado en otro. Además de la variable de contador requiere establecer la condición que permita salir del bucle cuando se llegue al final de la iteración, la condición es muy importante para no crear un bucle infinito.

1
2
3
for (int i = 0; i < 5; ++i) {
    System.out.println(i);
}
For.java
1
2
3
4
5
Collection<Integer> = Arrays.asList(0, 1, 2, 3, 4);
Iterable it = collection.iterable();
while (it.hasNext()) {
    System.out.println(it.next());
}
Iterator.java

Bucle foreach

En Java 5 el bucle for se enriqueció notablemente, el bucle foreach es un bucle for mejorado con el que se puede recorrer una colección y cualquier objeto que implemente la interfaz Iterable. Este bucle tiene la ventaja de que no hay que mantener una variable que haga de contador ni requiere establecer una condición para comprobar si se ha llegado al final de la iteración, esto evita la posibilidad de crear un bucle infinito. Con el bucle foreach una Collection se recorre de la siguiente manera.

1
2
3
for (int i : Arrays.asList(0, 1, 2, 3, 4)) {
    logger.info("{}", i);
}
Foreach.java

Bucle con Iterable

Pero el forearch es para las colecciones si se quiere hacer un bucle de un número fijo de iteraciones como en el primer caso, de 0 a 5, conociendo que para usar el foreach basta que le indiquemos un objeto que implemente la interfaz Iterable podemos usar la siguiente expresión y su implementación que tiene la ventaja de no tener que incluir el valir inicial del contador, la expresión de condición y el incremento o decremento de la variable. La clase Counter implementa la interfaz Iterable y devuelve un Iterator sobre los valores del rango indicado.

1
2
3
for (Object i : new Counter(0, 5)) {
    System.out.println(i);
}
CounterIterable.java
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package io.github.picodotdev.blogbitix.javaforeach;

import java.util.Iterator;

public class Counter implements Iterable<Integer> {

    private int start;
    private int end;

    public Counter(int start, int end) {
        this.start = start;
        this.end = end;
    }

    public Iterator<Integer> iterator() {
        return new Iterator<Integer>() {
            private int i = start;

            public boolean hasNext() {
                return i < end;
            }

            public Integer next() {
                return i++;
            }

            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }
}
Counter.java

Bucle con streams

En Java 8 con la introducción de los Stream y de IntStream podemos usar el método range y rangeClosed para obtener un Stream de enteros y hacer un bucle con un comportamiento similar a los anteriores.

1
2
3
IntStream.range(0, 5).forEach( i -> {
    System.out.println(i);
});
Stream.java

Los Stream de Java 8 están muy bien para simplificar algunas operaciones complejas pero para un bucle for sencillo tiene sus inconvenientes como ofuscar significativamente el stacktrace en caso de producirse alguna excepción. Se puede usar cualquier opción pero la primera con el tradicional bucle for sea la menos recomendable teniendo a nuestra disposición la clase Counter con Java 5 o los Stream y lambdas con Java 8.

Ejemplo con los distintos tipos de bucle

El siguiente programa muestra las cuatro opciones, su salida en la consola sería el siguiente:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package io.github.picodotdev.blogbitix.javaforeach;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Arrays;
import java.util.stream.IntStream;

public class Main {

    private static Logger logger = LoggerFactory.getLogger(Main.class);

    public static void main(String[] args) {
        logger.info("for i");
        for (int i = 0; i < 5; i++) {
            logger.info("{}", i);
        }

        logger.info("foreach");
        for (int i : Arrays.asList(0, 1, 2, 3, 4)) {
            logger.info("{}", i);
        }

        logger.info("for counter");
        for (int i : new Counter(0, 5)) {
            logger.info("{}", i);
        }

        logger.info("stream foreach");
        IntStream.range(0, 5).forEach(i -> {
            logger.info("{}", i);
        });
    }
}
Main.java

Para cualquiera de las formas de hacer el bucle for el comportamiento es el mismo, iterar un número finito de veces o sobre los elementos de una colección. Elegir cual usar entre los diferentes tipos de bucles depende del caso y de las preferencias personales pero también considerando la legibilidad y expresividad del código fuente.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
$ ./gradlew run

> Task :run
20:58:11.980 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main for i
20:58:11.981 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 0
20:58:11.981 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 1
20:58:11.981 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 2
20:58:11.981 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 3
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 4
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main foreach
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 0
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 1
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 2
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 3
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 4
20:58:11.982 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main for counter
20:58:11.983 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 0
20:58:11.983 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 1
20:58:11.983 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 2
20:58:11.983 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 3
20:58:11.983 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 4
20:58:11.983 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main stream foreach
20:58:11.985 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 0
20:58:11.985 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 1
20:58:11.985 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 2
20:58:11.985 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 3
20:58:11.985 [main] INFO  io.github.picodotdev.blogbitix.javaforeach.Main 4

BUILD SUCCESSFUL in 5s
3 actionable tasks: 3 executed
System.out

El código fuente completo del ejemplo puedes descargarlo del repositorio de ejemplos de Blog Bitix alojado en GitHub y probarlo en tu equipo ejecutando siguiente comando:
./gradlew run

Comparte el artículo: