Bucles en Java — práctica con texto, estadísticas y patrones

Hay un ejercicio del laboratorio de FP2 que parece trivial hasta que lo intentas: contar cuántas vocales tiene una frase. En Python era for c in texto: y ya está, sin pensar en nada más. En Java lo primero que haces, casi por instinto, es escribir for (char c : texto) y el compilador te devuelve un error que no entiendes: «for-each not applicable to type ‘String'». Ahí es cuando te acuerdas de que en Java un String no se puede recorrer directamente, y hay que buscar la vuelta.

Esa vuelta es justo el tema de esta práctica. Vamos a recorrer texto carácter a carácter de las dos formas que existen en Java, y de paso vas a escribir tres programas que combinan todo lo que ya sabes de bucles: for, for-each, while y do-while.

Abre VS Code, crea una carpeta practica_bucles y un fichero .java para cada programa.

Bucles en Java práctica — Programa 1: Analizador de texto

Este programa cuenta letras, vocales, dígitos y espacios de una frase, usando las dos formas de recorrer un String que existen en Java. Es el ejercicio que resuelve la duda del principio.

import java.util.Scanner;

public class AnalizadorTexto {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== ANALIZADOR DE TEXTO ===");
        System.out.println();

        System.out.print("Escribe una frase: ");
        String frase = sc.nextLine();

        // FORMA 1 — con charAt() y un for con índice
        // Es la más directa cuando necesitas saber también la posición
        int letras = 0, digitos = 0, espacios = 0, mayusculas = 0;

        for (int i = 0; i < frase.length(); i++) {
            char c = frase.charAt(i);    // el carácter en la posición i

            if (Character.isLetter(c)) {
                letras++;
                if (Character.isUpperCase(c)) {
                    mayusculas++;
                }
            } else if (Character.isDigit(c)) {
                digitos++;
            } else if (c == ' ') {
                espacios++;
            }
        }

        // FORMA 2 — con toCharArray() y for-each
        // Más limpia cuando solo te interesa el carácter, no su posición
        int vocales = 0;
        for (char c : frase.toLowerCase().toCharArray()) {
            // toLowerCase() antes de comparar — así "A" y "a" cuentan igual
            if (c == 'a' || c == 'e' || c == 'i' || c == 'o' || c == 'u') {
                vocales++;
            }
        }

        System.out.println();
        System.out.println("--- Resultado ---");
        System.out.printf("Longitud total:  %d caracteres%n", frase.length());
        System.out.printf("Letras:          %d%n", letras);
        System.out.printf("  De las cuales, mayúsculas: %d%n", mayusculas);
        System.out.printf("Vocales:         %d%n", vocales);
        System.out.printf("Dígitos:         %d%n", digitos);
        System.out.printf("Espacios:        %d%n", espacios);

        // Palíndromo — comprobamos leyendo desde los dos extremos a la vez
        String limpio = frase.toLowerCase().replace(" ", "");
        boolean esPalindromo = true;
        for (int i = 0; i < limpio.length() / 2; i++) {
            // Comparamos el carácter i-ésimo desde el principio con el i-ésimo desde el final
            if (limpio.charAt(i) != limpio.charAt(limpio.length() - 1 - i)) {
                esPalindromo = false;
                break;    // en cuanto encontramos una diferencia, no hace falta seguir
            }
        }
        System.out.printf("¿Es palíndromo?  %s%n", esPalindromo ? "Sí" : "No");

        sc.close();
    }
}

Pruébalo con «Anita lava la tina» y confirma que el programa lo detecta como palíndromo. El bucle que lo comprueba solo recorre hasta la mitad del texto (limpio.length() / 2) porque comparar la segunda mitad sería repetir el mismo trabajo dos veces — si el primer carácter coincide con el último, y el segundo con el penúltimo, ya sabes que el resto también coincidirá si sigues ese patrón.

for (char c : frase) no compila. Un String no es directamente recorrible con for-each en Java — solo lo son los arrays y las colecciones. Para usar for-each con texto, primero tienes que convertirlo con frase.toCharArray(), que sí devuelve un array de char.

Bucles en Java práctica — Programa 2: Estadísticas de un array de notas

Aquí trabajas con bucles sobre números en lugar de texto: mínimo, máximo, media y desviación respecto a la media, todo con for-each y for con índice según lo que necesites en cada momento.

import java.util.Scanner;

public class EstadisticasNotas {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== ESTADÍSTICAS DE NOTAS ===");
        System.out.println();

        System.out.print("¿Cuántas notas vas a introducir? ");
        int n = sc.nextInt();
        double[] notas = new double[n];

        for (int i = 0; i < n; i++) {
            System.out.printf("Nota %d: ", i + 1);
            notas[i] = sc.nextDouble();
        }

        // Suma con for-each — no necesitamos el índice, solo el valor
        double suma = 0;
        for (double nota : notas) {
            suma += nota;
        }
        double media = suma / n;

        // Mínimo y máximo — empezamos asumiendo que el primero es ambos
        // y vamos comparando con el resto
        double minimo = notas[0];
        double maximo = notas[0];
        for (double nota : notas) {
            if (nota < minimo) minimo = nota;
            if (nota > maximo) maximo = nota;
        }

        // Cuántas notas se alejan más de 1 punto de la media
        // Aquí sí necesitamos el índice, para poder decir cuál es
        int alejadas = 0;
        for (int i = 0; i < n; i++) {
            double distancia = Math.abs(notas[i] - media);
            if (distancia > 1.0) {
                alejadas++;
            }
        }

        System.out.println();
        System.out.println("--- Resultado ---");
        System.out.printf("Media:    %.2f%n", media);
        System.out.printf("Mínimo:   %.2f%n", minimo);
        System.out.printf("Máximo:   %.2f%n", maximo);
        System.out.printf("Rango:    %.2f%n", maximo - minimo);
        System.out.printf("Notas a más de 1 punto de la media: %d de %d%n", alejadas, n);

        sc.close();
    }
}

Fíjate en el patrón para el mínimo y el máximo: no hace falta ningún truco especial, solo asumir que el primer elemento es el mínimo y el máximo a la vez, y corregir esa suposición según vas mirando el resto del array. Es el mismo patrón que usarás para buscar el elemento más caro o más barato en cualquier lista de datos.

Cuando necesites el valor de cada elemento y nada más, usa for-each. Cuando necesites saber en qué posición está algo, o comparar una posición con otra (como con el palíndromo del programa anterior), necesitas el índice, así que usa el for con contador.

Bucles en Java práctica — Programa 3: Generador de patrones con validación

El clásico de los bucles anidados, pero con una vuelta de tuerca: el usuario elige qué patrón quiere ver con un menú, así que combinas do-while, switch y bucles for anidados en un mismo programa.

import java.util.Scanner;

public class GeneradorPatrones {
    public static void main(String[] args) {
        Scanner sc = new Scanner(System.in);

        System.out.println("=== GENERADOR DE PATRONES ===");

        int opcion;
        do {
            System.out.println("\n1. Triángulo");
            System.out.println("2. Pirámide");
            System.out.println("3. Tablero de ajedrez");
            System.out.println("4. Salir");
            System.out.print("Opción: ");
            opcion = sc.nextInt();

            if (opcion == 4) {
                System.out.println("Hasta luego");
                break;    // salimos del do-while sin pedir el tamaño
            }

            System.out.print("Tamaño (3-10): ");
            int n = sc.nextInt();

            // Validamos antes de dibujar nada — si el tamaño no vale, avisamos y volvemos al menú
            if (n < 3 || n > 10) {
                System.out.println("✗ El tamaño debe estar entre 3 y 10");
                continue;    // salta directamente a la siguiente vuelta del do-while
            }

            System.out.println();
            switch (opcion) {
                case 1:
                    for (int i = 1; i <= n; i++) {
                        for (int j = 0; j < i; j++) {
                            System.out.print("* ");
                        }
                        System.out.println();
                    }
                    break;

                case 2:
                    for (int i = 1; i <= n; i++) {
                        // Espacios para centrar — decrecen mientras los asteriscos crecen
                        for (int s = 0; s < n - i; s++) {
                            System.out.print(" ");
                        }
                        for (int j = 0; j < i; j++) {
                            System.out.print("* ");
                        }
                        System.out.println();
                    }
                    break;

                case 3:
                    for (int i = 0; i < n; i++) {
                        for (int j = 0; j < n; j++) {
                            // (i + j) par en una casilla, impar en la otra — el truco del ajedrez
                            if ((i + j) % 2 == 0) {
                                System.out.print("■ ");
                            } else {
                                System.out.print("□ ");
                            }
                        }
                        System.out.println();
                    }
                    break;

                default:
                    System.out.println("✗ Opción no válida");
            }
        } while (true);

        sc.close();
    }
}

El (i + j) % 2 == 0 del tablero de ajedrez es el patrón que más cuesta ver a primera vista, así que merece una explicación aparte. Cuando la suma de la fila y la columna es par, pintamos una casilla; cuando es impar, la otra. Como fila y columna van cambiando juntas en los dos bucles anidados, ese resultado par-impar se va alternando solo, casilla a casilla, y así aparece el patrón de ajedrez sin tener que llevar la cuenta de nada más.

continue salta directamente a la siguiente vuelta del bucle sin ejecutar el resto del código de esa vuelta. En este programa, si el tamaño no es válido, continue te devuelve al principio del do-while — es decir, al menú — sin intentar dibujar nada con un tamaño incorrecto.

Visualízalo con Python Tutor

Selecciona Java en el desplegable y pega en pythontutor.com:

public class Demo {
    public static void main(String[] args) {
        String texto = "ana";

        // Comprobación de palíndromo, paso a paso
        boolean esPalindromo = true;
        for (int i = 0; i < texto.length() / 2; i++) {
            char desdeElPrincipio = texto.charAt(i);
            char desdeElFinal = texto.charAt(texto.length() - 1 - i);
            if (desdeElPrincipio != desdeElFinal) {
                esPalindromo = false;
                break;
            }
        }
        System.out.println("¿Palíndromo? " + esPalindromo);

        // Mínimo y máximo de un array
        int[] numeros = {7, 2, 9, 4};
        int minimo = numeros[0];
        int maximo = numeros[0];
        for (int n : numeros) {
            if (n < minimo) minimo = n;
            if (n > maximo) maximo = n;
        }
        System.out.println("Mínimo: " + minimo + ", Máximo: " + maximo);
    }
}

Ejecuta paso a paso y fíjate en dos cosas. En la comprobación del palíndromo, observa cómo texto.length() / 2 limita el bucle a una sola vuelta con «ana» (longitud 3, así que solo compara la posición 0 con la 2, y la posición del medio no necesita comparación consigo misma). Y en el mínimo y máximo, mira cómo minimo y maximo empiezan los dos con el mismo valor, 7, y se van separando según el for-each va revisando 2, 9 y 4 uno detrás de otro.

Resumen y siguiente paso

En esta práctica recorriste texto de las dos formas posibles en Java, calculaste estadísticas sobre un array combinando for y for-each según lo que necesitabas en cada momento, y controlaste un menú completo con do-while, switch, break y continue trabajando juntos. La próxima vez que el compilador te diga que no puedes hacer for-each sobre un String, ya sabes exactamente qué falta.

En el siguiente artículo tienes ejercicios para resolver por tu cuenta.

Siguiente paso: ejerciciosBucles en Java — ejercicios con vocales, notas y el bucle que se me colgó a mí

Sigue aprendiendo

Deja una respuesta

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