Ir al contenido principal

Bucles: for, while, do-while

Introducción

Los bucles son estructuras fundamentales en la programación que nos permiten ejecutar un bloque de código repetidamente mientras se cumpla una determinada condición. Gracias a ellos, podemos automatizar tareas repetitivas, procesar colecciones de datos y crear algoritmos eficientes sin tener que escribir el mismo código una y otra vez.

En Java, disponemos de tres tipos principales de bucles: for, while y do-while, cada uno con características particulares que los hacen más adecuados para diferentes situaciones. En este artículo aprenderás cómo funcionan estos bucles, cuándo utilizar cada uno de ellos y las mejores prácticas para implementarlos correctamente en tus programas.

El bucle for

El bucle for es probablemente el más utilizado en Java cuando conocemos de antemano el número de iteraciones que necesitamos realizar o cuando trabajamos con colecciones.

Sintaxis básica

for (inicialización; condición; incremento) {
    // Código a ejecutar en cada iteración
}

Los componentes del bucle for son:

  • Inicialización: Se ejecuta una sola vez al inicio del bucle.
  • Condición: Se evalúa antes de cada iteración; si es true, el bucle continúa.
  • Incremento: Se ejecuta después de cada iteración.

Ejemplo simple

public class EjemploFor {
    public static void main(String[] args) {
        // Imprimir los números del 1 al 5
        for (int i = 1; i <= 5; i++) {
            System.out.println("Número: " + i);
        }
    }
}

Cuando ejecutes este programa, verás:

Número: 1
Número: 2
Número: 3
Número: 4
Número: 5

Bucle for en orden descendente

También podemos contar hacia atrás utilizando un bucle for:

public class ForDescendente {
    public static void main(String[] args) {
        // Cuenta regresiva desde 10 hasta 1
        for (int i = 10; i >= 1; i--) {
            System.out.println("Cuenta atrás: " + i);
        }
        System.out.println("¡Despegue!");
    }
}

Bucle for con incremento personalizado

Podemos modificar el incremento para saltar números:

public class ForIncrementoPersonalizado {
    public static void main(String[] args) {
        // Imprimir los números pares del 2 al 10
        for (int i = 2; i <= 10; i += 2) {
            System.out.println("Número par: " + i);
        }
    }
}

Bucle for anidado

Los bucles for pueden anidarse para trabajar con estructuras bidimensionales como matrices:

public class ForAnidado {
    public static void main(String[] args) {
        // Crear un patrón de asteriscos (una matriz de 5x5)
        for (int fila = 1; fila <= 5; fila++) {
            for (int columna = 1; columna <= 5; columna++) {
                System.out.print("* ");
            }
            System.out.println(); // Salto de línea al final de cada fila
        }
    }
}

Este código imprimirá un cuadrado de asteriscos de 5x5:

* * * * * 
* * * * * 
* * * * * 
* * * * * 
* * * * * 

El bucle while

El bucle while ejecuta un bloque de código mientras una condición específica sea verdadera. A diferencia del bucle for, no tiene una variable de control integrada, por lo que es ideal cuando no sabemos exactamente cuántas iteraciones necesitaremos.

Sintaxis básica

while (condición) {
    // Código a ejecutar mientras la condición sea verdadera
}

Ejemplo simple

public class EjemploWhile {
    public static void main(String[] args) {
        int contador = 1;
        
        while (contador <= 5) {
            System.out.println("Iteración número: " + contador);
            contador++; // Incrementamos el contador para evitar un bucle infinito
        }
    }
}

Ejemplo práctico: adivinar un número

import java.util.Scanner;
import java.util.Random;

public class JuegoAdivinanza {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        Random random = new Random();
        
        int numeroSecreto = random.nextInt(100) + 1; // Número entre 1 y 100
        int intentos = 0;
        int adivinanza = 0;
        
        System.out.println("He pensado un número entre 1 y 100. ¡Adivínalo!");
        
        while (adivinanza != numeroSecreto) {
            System.out.print("Introduce tu número: ");
            adivinanza = scanner.nextInt();
            intentos++;
            
            if (adivinanza < numeroSecreto) {
                System.out.println("El número secreto es mayor.");
            } else if (adivinanza > numeroSecreto) {
                System.out.println("El número secreto es menor.");
            } else {
                System.out.println("¡Correcto! Has adivinado el número en " + intentos + " intentos.");
            }
        }
        
        scanner.close();
    }
}

En este juego de adivinanza, no sabemos cuántos intentos necesitará el usuario para adivinar el número, por lo que un bucle while es más apropiado que un for.

El bucle do-while

El bucle do-while es similar al while, pero con una diferencia crucial: la condición se evalúa al final de cada iteración, lo que garantiza que el bloque de código se ejecute al menos una vez, incluso si la condición inicialmente es falsa.

Sintaxis básica

do {
    // Código a ejecutar al menos una vez y luego mientras la condición sea verdadera
} while (condición);

Ejemplo simple

public class EjemploDoWhile {
    public static void main(String[] args) {
        int contador = 1;
        
        do {
            System.out.println("Iteración número: " + contador);
            contador++;
        } while (contador <= 5);
    }
}

Ejemplo práctico: menú de opciones

import java.util.Scanner;

public class MenuOpciones {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        int opcion;
        
        do {
            System.out.println("\n--- MENÚ PRINCIPAL ---");
            System.out.println("1. Ver usuarios");
            System.out.println("2. Añadir usuario");
            System.out.println("3. Eliminar usuario");
            System.out.println("0. Salir");
            System.out.print("Selecciona una opción: ");
            
            opcion = scanner.nextInt();
            
            switch (opcion) {
                case 1:
                    System.out.println("Mostrando lista de usuarios...");
                    break;
                case 2:
                    System.out.println("Añadiendo nuevo usuario...");
                    break;
                case 3:
                    System.out.println("Eliminando usuario...");
                    break;
                case 0:
                    System.out.println("Saliendo del programa...");
                    break;
                default:
                    System.out.println("Opción no válida. Inténtalo de nuevo.");
            }
            
        } while (opcion != 0);
        
        scanner.close();
        System.out.println("¡Programa finalizado!");
    }
}

Este ejemplo muestra un menú que se presenta al menos una vez, y sigue mostrándose después de cada operación hasta que el usuario elija la opción de salir. El bucle do-while es perfecto para este tipo de situaciones.

El bucle for-each

Desde Java 5, se introdujo el bucle for mejorado, también conocido como bucle "for-each". Esta estructura está diseñada específicamente para recorrer colecciones y arrays de manera más sencilla y legible.

Sintaxis básica

for (TipoDato elemento : coleccion) {
    // Código a ejecutar para cada elemento
}

Ejemplo con array

public class ForEachEjemplo {
    public static void main(String[] args) {
        String[] frutas = {"Manzana", "Plátano", "Naranja", "Fresa", "Kiwi"};
        
        // Recorremos el array con for-each
        for (String fruta : frutas) {
            System.out.println("Me gusta la " + fruta.toLowerCase());
        }
    }
}

Este código es más claro y conciso que el equivalente con un bucle for tradicional:

for (int i = 0; i < frutas.length; i++) {
    System.out.println("Me gusta la " + frutas[i].toLowerCase());
}

La principal limitación del bucle for-each es que no tenemos acceso directo al índice del elemento, y no podemos modificar la colección mientras la recorremos.

Control de flujo en bucles

Java proporciona varias instrucciones para controlar el flujo de ejecución dentro de los bucles:

break

La instrucción break termina inmediatamente el bucle más interno que la contiene y continúa la ejecución desde la siguiente línea después del bucle.

public class EjemploBreak {
    public static void main(String[] args) {
        for (int i = 1; i <= 10; i++) {
            if (i == 6) {
                System.out.println("¡Encontrado el número 6! Saliendo del bucle...");
                break; // Sale del bucle cuando i es 6
            }
            System.out.println("Número actual: " + i);
        }
        System.out.println("Bucle finalizado");
    }
}

continue

La instrucción continue salta a la siguiente iteración del bucle más interno, omitiendo el resto del código en la iteración actual.

public class EjemploContinue {
    public static void main(String[] args) {
        // Imprimir solo los números impares del 1 al 10
        for (int i = 1; i <= 10; i++) {
            if (i % 2 == 0) {
                continue; // Salta los números pares
            }
            System.out.println("Número impar: " + i);
        }
    }
}

Etiquetas (labels)

Las etiquetas permiten controlar bucles anidados especificando a qué bucle nos referimos con las instrucciones break y continue.

public class EjemploEtiquetas {
    public static void main(String[] args) {
        // Buscar una coordenada específica en una matriz 5x5
        int filaObjetivo = 3;
        int columnaObjetivo = 2;
        boolean encontrado = false;
        
        bucleExterno: // Etiqueta para el bucle externo
        for (int fila = 1; fila <= 5; fila++) {
            for (int columna = 1; columna <= 5; columna++) {
                System.out.println("Comprobando posición: [" + fila + "," + columna + "]");
                
                if (fila == filaObjetivo && columna == columnaObjetivo) {
                    System.out.println("¡Objetivo encontrado en [" + fila + "," + columna + "]!");
                    encontrado = true;
                    break bucleExterno; // Sale de ambos bucles
                }
            }
        }
        
        if (encontrado) {
            System.out.println("Búsqueda exitosa.");
        } else {
            System.out.println("Objetivo no encontrado.");
        }
    }
}

En este ejemplo, la etiqueta bucleExterno nos permite salir de ambos bucles a la vez cuando encontramos nuestro objetivo, en lugar de salir solo del bucle interno.

Bucles infinitos y cómo evitarlos

Un bucle infinito es aquel que nunca termina porque su condición de salida nunca se cumple. Aunque a veces son útiles (por ejemplo, en sistemas que deben ejecutarse continuamente), generalmente son errores de programación.

Ejemplo de bucle infinito

// No ejecutes este código sin una estrategia para detenerlo
while (true) {
    System.out.println("¡Este mensaje se imprimirá infinitamente!");
}

Para evitar bucles infinitos:

  1. Asegúrate de que la condición del bucle eventualmente cambie a falsa
  2. Verifica que las variables de control se actualicen correctamente
  3. Considera añadir una condición de salida de seguridad o un contador límite
// Ejemplo seguro: bucle con límite de iteraciones
int contador = 0;
int limite = 1000000;

while (true) {
    System.out.println("Iteración: " + contador);
    contador++;
    
    if (contador >= limite) {
        System.out.println("Alcanzado límite de seguridad. Saliendo del bucle.");
        break;
    }
}

Ejemplo integrado: Procesamiento de notas

Veamos un ejemplo completo que integra varios tipos de bucles para procesar un conjunto de notas de estudiantes:

import java.util.Scanner;

public class ProcesadorNotas {
    public static void main(String[] args) {
        Scanner scanner = new Scanner(System.in);
        
        System.out.print("¿Cuántos estudiantes hay en la clase? ");
        int numeroEstudiantes = scanner.nextInt();
        
        // Array para almacenar las notas
        double[] notas = new double[numeroEstudiantes];
        
        // Bucle for para solicitar las notas
        for (int i = 0; i < numeroEstudiantes; i++) {
            System.out.print("Introduce la nota del estudiante " + (i + 1) + ": ");
            double nota;
            
            // Bucle do-while para validar que la nota esté entre 0 y 10
            do {
                nota = scanner.nextDouble();
                if (nota < 0 || nota > 10) {
                    System.out.print("La nota debe estar entre 0 y 10. Inténtalo de nuevo: ");
                }
            } while (nota < 0 || nota > 10);
            
            notas[i] = nota;
        }
        
        // Calcular estadísticas usando un bucle while
        int i = 0;
        double suma = 0;
        double notaMaxima = notas[0];
        double notaMinima = notas[0];
        
        while (i < notas.length) {
            suma += notas[i];
            
            if (notas[i] > notaMaxima) {
                notaMaxima = notas[i];
            }
            
            if (notas[i] < notaMinima) {
                notaMinima = notas[i];
            }
            
            i++;
        }
        
        double media = suma / numeroEstudiantes;
        
        // Mostrar las notas usando for-each
        System.out.println("\nNotas registradas:");
        int estudianteNum = 1;
        for (double nota : notas) {
            System.out.println("Estudiante " + estudianteNum + ": " + nota);
            estudianteNum++;
        }
        
        // Mostrar estadísticas
        System.out.println("\nEstadísticas:");
        System.out.println("Media de la clase: " + media);
        System.out.println("Nota más alta: " + notaMaxima);
        System.out.println("Nota más baja: " + notaMinima);
        
        scanner.close();
    }
}

Este ejemplo muestra:

  • Uso de for para recorrer un array con acceso al índice
  • Uso de do-while para validación de entrada
  • Uso de while para cálculos acumulativos
  • Uso de for-each para mostrar resultados

Comparación de los diferentes tipos de bucles

Bucle Cuando usarlo Ventajas Desventajas
for Cuando sabes el número exacto de iteraciones Compacto, con inicialización y actualización integradas Menos flexible para condiciones complejas
while Cuando no sabes cuántas iteraciones necesitarás Simple y claro cuando la condición es lo más importante Requiere inicialización externa y actualización manual
do-while Cuando necesitas ejecutar el código al menos una vez Garantiza al menos una ejecución Puede ser confuso para principiantes
for-each Para recorrer colecciones completas Muy legible y conciso No se puede acceder al índice ni modificar la colección

Resumen

Los bucles son estructuras fundamentales que nos permiten ejecutar código repetidamente mientras se cumpla una condición determinada. En Java, disponemos principalmente de tres tipos de bucles: for, while y do-while, además del bucle for-each para recorrer colecciones.

El bucle for es ideal cuando conocemos el número de iteraciones de antemano, el bucle while cuando la condición de continuación es lo más importante, y el bucle do-while cuando necesitamos que el código se ejecute al menos una vez. Además, disponemos de instrucciones como break y continue para controlar el flujo dentro de los bucles.

Elegir el tipo de bucle adecuado para cada situación y aplicar buenas prácticas nos ayudará a escribir código más eficiente, legible y mantenible. En el próximo artículo, exploraremos las sentencias de salto en Java, que complementan a los bucles y nos permiten controlar de manera más precisa el flujo de ejecución en nuestros programas.