Control de flujo de Java: bucles para y para cada

    Introducci贸n

    Las declaraciones condicionales y los bucles son una herramienta muy importante en la programaci贸n. No hay muchas cosas que podamos hacer con c贸digo que solo se pueda ejecutar l铆nea por l铆nea.

    Eso es lo que significa “control de flujo”: guiar la ejecuci贸n de nuestro programa, en lugar de dejar que se ejecute l铆nea por l铆nea independientemente de cualquier factor interno o externo. Cada lenguaje de programaci贸n admite alguna forma de control de flujo, si no expl铆citamente a trav茅s de ifsy fors o declaraciones similares, entonces impl铆citamente nos da las herramientas para crear tales construcciones, es decir, los lenguajes de programaci贸n de bajo nivel generalmente logran ese efecto con una gran cantidad de go-to comandos.

    Los bucles eran un concepto que se usaba mucho antes de que la programaci贸n de computadoras existiera, pero la primera persona en usar un bucle de software fue Ada Lovelace, com煤nmente conocida por su apellido de soltera, Byron, mientras calculaba N煤meros de Bernoulli, all谩 por el siglo XIX.

    En Java, hay varias formas de controlar el flujo del c贸digo:

    • declaraciones if y if-else
    • cambiar declaraciones
    • declaraciones while y do-while
    • for y enhanced for bucles
    • break y continue declaraciones

    El bucle for

    for Los bucles se utilizan normalmente cuando el n煤mero de iteraciones es “fijo” de alguna manera. O sabemos exactamente cu谩ntas veces se ejecutar谩 el ciclo o tenemos una idea mejor que “hasta que n se convierta en m”.

    Los bucles for se utilizan a menudo con matrices:

    for (initialization; terminationCondition; update) {
        // Code here...
    }
    

    Y una implementaci贸n simple:

    int[] arr = {1,2,3,4,5,6,7,8,9};
    
    for (int i = 0; i < arr.length; i++) {
        System.out.println(arr[i]);
    }
    

    Esta es una muy simple for declaraci贸n, y as铆 es como se ejecuta:

    • Establecer la variable local i ser 0
    • Compruebe si i es menos que arr.length, si procede dentro del bloque
      2.1. Imprimir arr[i]
      2.2. Incremento i en 1, vaya al paso 2.
    • Si i no es menos que arr.length, proceda despu茅s del bloque

    Tan pronto como el paso 2 encuentre que i es mayor o igual a arr.length, los bucles detienen su ejecuci贸n y Java contin煤a la ejecuci贸n desde la l铆nea que sigue al bloque del bucle.

    Nota: Aqu铆, la ubicaci贸n del operador de incremento (++) no es importante. Si nuestro paso de actualizaci贸n fue ++i en cambio, nada cambiar铆a ya que el paso de actualizaci贸n siempre se ejecuta despu茅s del c贸digo en el for bloque de bucle.

    Este c贸digo, por supuesto, se imprimir铆a:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    

    Hay tres bloques diferentes utilizados en el for bucle que nos permite hacer esto: el bloque de inicializaci贸n, la condici贸n de terminaci贸n y el paso de actualizaci贸n.

    Bloque de inicializaci贸n

    Un bloque de inicializaci贸n en el for El bucle se utiliza para inicializar una variable. En nuestro ejemplo, quer铆amos la variable i para comenzar en 0, ya que 0 es el primer 铆ndice de una matriz.

    Este bloque se ejecuta solo una vez, al inicio del for lazo. Tambi茅n podemos definir m煤ltiples variables del mismo tipo aqu铆:

    // Single variable
    for (int i = 0; i < 10; i++) {
        // Code
    }
    
    // Multiple variables
    for (int i = 10, j = 25; i < arr.length; i++) {
        // Code
    }
    
    // Multiple variables
    for (int i = 10, double j = 25.5; i < arr.length; i++) {
        // WON'T compile because we used two different types - int and double
    }
    

    Las variables inicializadas dentro del for La declaraci贸n solo se puede usar dentro del for bloquear. Acceder a ellos fuera de su alcance resultar谩 en una excepci贸n, como se esperaba:

    for (int i = 0; i < 10; i++) {
        System.out.println(i);
    }
    System.out.println(i);
    

    La variable i se ha hecho referencia fuera del alcance:

    MyClass.java:6: error: cannot find symbol
    System.out.println(i);
                       ^
      symbol:   variable i
      location: class MyClass
    1 error
    

    Nota: El c贸digo en el bloque de inicializaci贸n es opcional y no tiene que incluirse. Sin embargo, el bloque tiene que serlo. Por tanto, podemos escribir algo como esto:

    int i = 0;
    for (; i < 10; i++) {
        System.out.println(i);
    }
    System.out.println("ni variable is " + i);
    

    Y dar铆a como resultado la misma salida que si el bloque de inicializaci贸n estuviera all铆, excepto que el i La variable ya no est谩 fuera de alcance despu茅s de ejecutar la for lazo:

    0
    1
    2
    3
    4
    5
    6
    7
    8
    9
    
    i variable is 10
    

    El bloque de inicializaci贸n est谩 t茅cnicamente ah铆, ya que incluimos el ; final de la misma, pero no hay c贸digo dentro de ella.

    Condici贸n de terminaci贸n

    La condici贸n de terminaci贸n le dice al for bucle para ejecutar el c贸digo siempre que sea true. Si la condici贸n de terminaci贸n se eval煤a como false, el paso de actualizaci贸n y el resto del for se omiten el bucle. Solo puede haber una condici贸n de terminaci贸n:

    for (int i = 0; i < 10; i++) {
        System.out.println("i = " + i + " | i < 10 is true");
    }
    
    // Compiles as there's only one termination condition,
    // although there's two operators - two relational and one logical
    for (int i = 6; i < 10 & i > 5; i++) {
        System.out.println("i = " + i + " | i < 10 is true");
    }
    
    // WON'T compile, multiple separate termination conditions
    for (int i = 0; i < 10, i > 5; i++) {
        System.out.println("i = " + i + " | i < 10 is true");
    }
    

    La salida del primer bucle:

    i = 0 | i < 10 is true
    i = 1 | i < 10 is true
    i = 2 | i < 10 is true
    i = 3 | i < 10 is true
    i = 4 | i < 10 is true
    i = 5 | i < 10 is true
    i = 6 | i < 10 is true
    i = 7 | i < 10 is true
    i = 8 | i < 10 is true
    i = 9 | i < 10 is true
    

    Al llegar 10 la condici贸n i < 10 ya no es true y el c贸digo deja de repetirse.

    Y la salida del segundo bucle:

    i = 6 | i < 10 is true
    i = 7 | i < 10 is true
    i = 8 | i < 10 is true
    i = 9 | i < 10 is true
    

    Nota: La condici贸n de terminaci贸n tambi茅n es opcional. Es v谩lido definir un for bucle sin una condici贸n de terminaci贸n. Sin embargo, excluir la condici贸n de terminaci贸n har谩 que el c贸digo se repita infinitamente, o hasta que StackOverflowError ocurre.

    for (int i = 0; ; i++) {
        System.out.println("Looping forever!");
    }
    

    Aunque no hay c贸digo en la condici贸n de terminaci贸n, debe incluir un punto y coma para marcar que ha decidido dejarlo vac铆o, de lo contrario, el c贸digo no se compilar谩.

    MyClass.java:3: error: ';' expected
        for (int i = 0; i++) {
                           ^
    1 error
    

    Paso de actualizaci贸n

    El paso de actualizaci贸n con mayor frecuencia disminuye / incrementa alg煤n tipo de variable de control (en nuestro caso – i) despu茅s de cada iteraci贸n del ciclo. Esencialmente, se asegura de que nuestra condici贸n de terminaci贸n se cumpla en alg煤n momento; en nuestro caso, se incrementa i hasta llegar a 10.

    El paso de actualizaci贸n solo se ejecutar谩 si la condici贸n de terminaci贸n se eval煤a como true, y despu茅s del c贸digo en el for Se ha ejecutado el bloque de bucle. Puede tener varios pasos de actualizaci贸n si lo desea, e incluso puede llamar a m茅todos:

    for (int i = 0; i < 10; i++) {
        // Code
    }
    
    int j = 10;
    
    for (int i = 0; i < 10; i++, j--) {
        System.out.println(i + " | " + j);
    }
    
    // Multiple variables
    for (int i = 10; i < arr.length; i++, doSomething()) {
        System.out.println("Hello from the for loop");
    }
    
    public static void doSomething() {
        System.out.println("Hello from another method");
    }
    

    Y la salida de este bucle ser铆a:

    Hello from the for loop
    Hello from another method!
    Hello from the for loop
    Hello from another method!
    Hello from the for loop
    Hello from another method!
    Hello from the for loop
    Hello from another method!
    

    Esto tambi茅n confirma que el paso de actualizaci贸n se ejecuta despu茅s del c贸digo dentro del bloque.

    Nota: El paso de actualizaci贸n tambi茅n es opcional, al igual que el bloque de inicializaci贸n y la condici贸n de terminaci贸n. Puede ser reemplazado por c贸digo dentro del for loop, y podr铆amos incrementar / disminuir la variable de control antes del c贸digo de esa manera tambi茅n.

    驴Vac铆o para bucle?

    Dado que los tres bloques de la for Los bucles son t茅cnicamente opcionales, podr铆amos, sin ning煤n problema, escribir esto for lazo:

    int i = 0;
    
    // This will compile, all blocks are "present" but no code is actually there
    for (;;) {
        if (i < 10)
            System.out.println(i);
        i++;
    }
    

    Si miras m谩s de cerca, esto realmente se parece a un ciclo while:

    int i = 0;
    
    while (i < arr.length) {
        System.out.println(i);
        i++;
    }
    

    Todo lo que se puede hacer con un while El bucle se puede hacer con un for loop, y viceversa, y la elecci贸n entre ellos se decide por la legibilidad y la conveniencia.

    for Los bucles y las tres partes proporcionan una descripci贸n clara de las condiciones del bucle, d贸nde comienza, c贸mo cambia el estado y cu谩ndo deja de iterar. Los hace muy concisos y f谩ciles de manipular.

    Dado esto, es muy recomendable evitar cambiar la variable de control (i en nuestro caso) fuera del par茅ntesis despu茅s de for, a menos que sea absolutamente necesario.

    Anidado para bucles

    Anidado for Los bucles tambi茅n son muy comunes, especialmente cuando se itera a trav茅s de matrices multidimensionales (matrices que tienen otras matrices como elementos):

    int[][] multiArr = {{1,2,3},{4},{5,6}};
    
    for (int i = 0; i < multiArr.length; i++) {
        for (int j = 0; j < multiArr[i].length; j++) {
            System.out.print(multiArr[i][j] + " ");
        }
        System.out.println();
    }
    

    Ejecutar este c贸digo producir铆a:

    1 2 3
    4
    5 6
    

    Mejorado para Loop

    El mejorado foro for-each es un tipo especial de for bucle que se puede utilizar para cualquier objeto que implemente el Iterable interfaz y matrices.

    Est谩 dise帽ado para pasar por los elementos en orden, uno tras otro, desde el principio hasta el final. Esto difiere del tradicional for bucle en el que podr铆amos haber dado el paso como quisi茅ramos; podr铆amos, por ejemplo, haber impreso todos los dem谩s elementos:

    int[] arr = {1,2,3,4,5,6,7,8,9};
    
    for (int i = 0; i < arr.length; i+=2) {
        System.out.println(arr[i]);
    }
    

    Observe que incrementamos i por 2 cada vez. Esto habr铆a impreso lo siguiente:

    1
    3
    5
    7
    9
    

    En cambio, con for-each iteramos a trav茅s de todos los elementos usando la siguiente sintaxis:

    for (ElementType localVar : somethingIterable) {
        // Code
    }
    

    El bucle almacena un elemento tras otro en el localVar variable, hasta que no queden m谩s elementos. Fue creado para evitar hacer tradicionales for bucles que pasaron por matrices / colecciones secuencialmente. Ya no necesitamos especificar un contador, establecer una posici贸n inicial y una posici贸n final, indexar manualmente la matriz y ya no tenemos que preocuparnos por los l铆mites, todo lo cual puede ser muy tedioso de escribir.

    Todo esto ha sido automatizado a trav茅s del for-each.

    List<String> list = new ArrayList<>(Arrays.asList("a","b","c"));
    
    for (String s : list) {
        System.out.println(s);
    }
    
    // The traditional for syntax...
    for (int i = 0; i < list.size(); i++) {
        System.out.println(list.get(i));
    }
    // ...or...
    for (Iterator<String> i = list.iterator(); i.hasNext();) {
        System.out.println(i.next());
    }
    

    El primer bucle es el m谩s limpio y requiere menos mantenimiento por nuestra parte. Este fragmento de c贸digo podr铆a leerse efectivamente como: “para cada cadena s en la lista de cadenas list, haz algo para s.

    Bucles anidados para cada uno

    for-each tambi茅n admite matrices multidimensionales, una de nuestras for bucles imprimieron elementos de una matriz bidimensional: as铆 es como se ver铆a usando for-each:

    int[][] multiArr = {{1,2,3},{4},{5,6}};
    
    // Keep in mind that x is an array, since multiArr
    // is an array of arrays
    for (int[] x : multiArr) {
        for (int y : x) {
            System.out.print(y + " ");
        }
        System.out.println();
    }
    

    Del mismo modo, podemos usarlo para iterar colecciones anidadas:

    ArrayList<String> stronglyTyped = new ArrayList<>();
    stronglyTyped.add("Java");
    stronglyTyped.add("Go");
    stronglyTyped.add("Harbour");
    stronglyTyped.add("Haskell");
    
    ArrayList<String> weaklyTyped = new ArrayList<>();
    weaklyTyped.add("C++");
    weaklyTyped.add("C");
    weaklyTyped.add("JavaScript");
    
    ArrayList<ArrayList<String>> programmingLanguages = new ArrayList<ArrayList<String>>();
    programmingLanguages.add(stronglyTyped);
    programmingLanguages.add(weaklyTyped);
    

    los programmingLanguages arraylist contiene otras dos listas de arrays – stronglyTyped y weaklyTyped.

    Para atravesarlos, simplemente escribir铆amos:

    for (ArrayList<String> languages : programmingLanguages) {
        for (String language : languages) {
            System.out.println(language);
        }
    }
    

    La salida:

    Java
    Go
    Harbour
    Haskell
    C++
    C
    JavaScript
    

    Modificaci贸n de valores durante para cada uno

    Es importante tener en cuenta que puede cambiar los valores de los elementos que est谩 iterando. Por ejemplo, en el ejemplo anterior, si cambia el System.out.println(language) con System.out.println(language.toUppercase()), ser铆amos recibidos con:

    JAVA
    GO
    HARBOUR
    HASKELL
    C++
    C
    JAVASCRIPT
    

    Esto se debe a que estamos tratando con objetos. Al iterar a trav茅s de ellos, estamos asignando sus variables de referencia al language String. Llamando a cualquier cambio en el language La variable de referencia tambi茅n se reflejar谩 en la original. En el caso de las cadenas, es posible que en realidad no afecte a los objetos debido al conjunto de cadenas, pero entiendes el punto.

    Esto, sin embargo, no sucede con las variables primitivas, ya que con ellas, el valor simplemente se copia en la variable a la que estamos accediendo. Entonces, cambiarlo no afectar谩 las variables originales.

    Bonus: para cada m茅todo

    Si bien no es el tema central de este art铆culo, debemos mencionar que hay una nueva forma de recorrer las listas desde Java 8. El .forEach() ahora est谩 disponible el m茅todo, que se puede combinar con expresiones lambda para bucles de una sola l铆nea.

    En algunos casos, esto se puede utilizar en lugar del for-each bucle, simplificando a煤n m谩s la iteraci贸n:

    list.forEach(x -> System.out.println(x));
    

    Conclusi贸n

    El control de flujo en el c贸digo es esencial en casi todas las aplicaciones. Las declaraciones que alteran el flujo del c贸digo son bloques de construcci贸n fundamentales y cada desarrollador aspirante debe tener el control total / ser consciente de c贸mo funcionan.

    for y for-each Los bucles son buenos para ejecutar un bloque de c贸digo un n煤mero conocido de veces, a menudo con una matriz u otro tipo de iterable. Tambi茅n son posibles bucles m谩s complejos, como incrementar el 铆ndice en 2, o incrementar y verificar m煤ltiples variables.

     

    Etiquetas:

    Deja una respuesta

    Tu direcci贸n de correo electr贸nico no ser谩 publicada. Los campos obligatorios est谩n marcados con *