8. Bucles anidados, matrices y sentencias de salto
UD3 — Control de Flujo y Depuración · RA3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje
| Aspecto | Valor |
|---|---|
| Resultado de aprendizaje | RA: 3 — Escribe y depura código, analizando y utilizando las estructuras de control del lenguaje. |
| Criterios de evaluación a los que contribuye esta sección | CE 3.2 (utiliza estructuras de repetición) CE 3.3 (reconoce las posibilidades de las sentencias de salto) CE 3.5 (crea programas ejecutables combinando estructuras de control y datos compuestos) CE 3.7 (comenta y documenta el código). |
| Marco normativo | RD 405/2023 · RD 450/2010 · RD 659/2023 |
| Tecnología base | Java 25 LTS (OpenJDK Temurin 25) — soporte hasta 2030+ |
| Horas estimadas | ~4 h (de las 30 h de la UD3). |
Requisitos previos (lo que ya sabes): condiciones booleanas y operadores lógicos (sección 1 de UD3);
if/else if/else, early return, validación conIO.readlny condicionales simples (sección 2 de UD3);switchexpression con flecha y múltiples etiquetas (sección 3 de UD3);whileydo-while(sección 4 de UD3); arrays unidimensionales como tipo compuesto (sección 5 de UD3);forclásico sobre arrays y Strings, incluyendo variantes (decreciente, saltos, múltiples variables) y utilidades deArrays(sección 6 de UD3);for-each(sección 7 de UD3).returndentro demainya lo usaste en early return (sección 2).
8.1 ¿De qué va esta sección? — Anidar, salir y saltar
Has llegado hasta aquí con tres bucles en el cinturón: while (sección 4), for clásico (sección 5) y for-each (sección 6). Ya sabes decidir cuándo usar cada uno y recorrer arrays unidimensionales sin problemas. Pero hay cuatro cosas que no se pueden hacer bien con un solo bucle:
-
Recorrer una tabla con filas y columnas. Hasta ahora solo has recorrido "filas" (un array de una dimensión). Si quieres una matriz (filas × columnas), necesitas un bucle dentro de otro: un bucle externo para las filas y un bucle interno para las columnas. Eso son los bucles anidados.
-
Salir del bucle antes de tiempo. Por ejemplo, cuando estás buscando un elemento y lo encuentras en la posición 3, ¿para qué seguir mirando hasta la posición 99? Querrías salir en cuanto lo veas. Para eso está
break. -
Saltarte una iteración sin ejecutar lo que queda. Por ejemplo, estás procesando una lista de números y, cuando uno es negativo, lo saltas (no lo sumas, no lo imprimes), pero continúas con el siguiente. Para eso está
continue. -
Salir de un bucle externo desde dentro del interno. Cuando combinas anidamiento y salida,
breaksolo sale del bucle más cercano. Para salir del externo desde dentro del interno existe elbreakcon etiqueta (labeled break).
Y finalmente hay una quinta sentencia de salto que ya conoces pero que aquí verás en su contexto natural: return dentro de un bucle, que sale del método completo (no solo del bucle).
En esta sección vas a aprender las matrices bidimensionales, los bucles anidados y las cuatro sentencias de salto. Cubriremos, en este orden:
- Matrices bidimensionales
int[][]: declaración, inicialización,matriz.lengthymatriz[i].length. - Bucles anidados: un bucle dentro de otro. Tablas, combinaciones, recorridos de matrices.
break: salir del bucle más cercano.continue: saltar a la siguiente iteración.returndentro de un bucle: salir del método completo.breakcon etiqueta (labeled break): salir de bucles anidados externos.- Cuándo los bucles anidados son legítimos y cuándo son un code smell.
- Errores típicos.
- Ejemplos resueltos paso a paso.
Analogía de la revisión médica
Imagina que eres un médico haciendo una revisión a un paciente. Recorres una lista de pruebas (un bucle). Cuando una prueba da positivo en algo grave, paras (eso es break): ya no necesitas seguir, tienes el diagnóstico. Si una prueba no se puede hacer porque el paciente no colaboró, te la saltas y pasas a la siguiente (eso es continue): no rompes el flujo, simplemente no la cuentas. Y si, en medio de la revisión, deciden ingresar al paciente, abandonas el método completo de "revisión" (eso es return): todo lo demás deja de importar.
Vamos a empezar por las matrices bidimensionales, que son la estructura natural para los bucles anidados.
8.2 Matrices bidimensionales int[][]
8.2.1 Qué es una matriz bidimensional
Una matriz bidimensional (en Java, int[][] o String[][] etc.) es un array de arrays. Es la forma natural de representar tablas con filas y columnas:
- Una hoja de cálculo con filas y columnas.
- Un tablero de ajedrez (8 × 8).
- Una imagen (píxeles en una rejilla).
- Una matriz matemática (3 × 3, 4 × 5, etc.).
Visualmente:
matriz[i][j] accede al elemento en la fila i, columna j. Los índices empiezan en 0, igual que en los arrays unidimensionales.
8.2.2 Declarar e inicializar con valores
La forma más simple de crear una matriz es con valores literales por filas:
Esto crea una matriz de 3 filas y 3 columnas. Cada { ... } interna es una fila.
Otro ejemplo, una matriz de 2 filas × 4 columnas:
8.2.3 Declarar con tamaño fijo
Si no conoces los valores iniciales, creas la matriz con tamaño fijo usando new:
Esto crea una matriz regular de 3 × 4 con todos los valores inicializados a 0 (valor por defecto de int).
8.2.4 matriz.length y matriz[i].length
Aquí hay un detalle importante:
matriz.lengthes el número de filas (porque una matrizint[][]es un array deint[], y.lengthdevuelve cuántos elementos tiene ese array externo).matriz[i].lengthes el número de columnas de la filai(porquematriz[i]es unint[]—un array unidimensional— y su.lengthes el tamaño de esa fila).
En matrices regulares (todas las filas tienen la misma longitud), todas las llamadas matriz[i].length devuelven el mismo número. En matrices dentadas (jagged arrays, donde cada fila tiene distinta longitud), cada fila puede tener su propio .length. Las matrices dentadas se ven en UD6.
8.2.5 Acceso por índice
Errores con los índices:
Igual que en arrays unidimensionales, los índices fuera de rango lanzan ArrayIndexOutOfBoundsException.
8.2.6 Tipos de matrices que puedes crear
Puedes hacer matrices de cualquier tipo, no solo int:
La sintaxis es siempre la misma: Tipo[][] nombre = { ... };.
También puedes tener matrices con distintos tamaños en las filas:
8.3 Bucles anidados: recorrer matrices y generar combinaciones
8.3.1 ¿Qué es un bucle anidado?
Un bucle anidado es un bucle dentro del bloque de otro bucle. Cada vez que el bucle externo da una iteración, el bucle interno recorre completo.
Salida:
Paso a paso:
- El bucle externo
ida 3 iteraciones (i=0, 1, 2). - Para cada iteración de
i, el bucle internojda 2 iteraciones (j=0, 1). - En total: 3 × 2 = 6 combinaciones.
8.3.2 La tabla de multiplicar
El ejemplo clásico de bucles anidados: imprimir la tabla de multiplicar del 1 al 5.
Salida:
Detalles:
- El bucle externo
irecorre las filas (1 a 5). - El bucle interno
jrecorre las columnas (1 a 5). \tes un tabulador: hace que las columnas queden alineadas (más o menos).IO.println()al final de cada iteración del externo: salto de línea para la siguiente fila.
8.3.3 Diagrama de flujo de bucles anidados
flowchart TD
Inicio([Inicio]) --> ExtInit["Inicializa bucle externo"]
ExtInit --> ExtCond{"¿Externa: hay más\niteraciones?"}
ExtCond -->|Sí| IntInit["Inicializa bucle interno"]
IntInit --> IntCond{"¿Interna: hay más\niteraciones?"}
IntCond -->|Sí| Cuerpo["Cuerpo del bucle interno\n(usas i y j)"]
Cuerpo --> IntUpdate["Update del interno"]
IntUpdate --> IntCond
IntCond -->|No| ExtUpdate["Update del externo"]
ExtUpdate --> ExtCond
ExtCond -->|No| Fin([Fin])
Fíjate: el bucle interno se ejecuta completo por cada iteración del externo. Para cada valor de i, se recorre todo el rango de j.
8.3.4 Recorrer una matriz con dos for clásicos
El patrón más típico: un for externo para las filas, un for interno para las columnas:
Salida:
Detalles clave:
matriz.lengthes el número de filas (3).matriz[i].lengthes el número de columnas de la filai. Si la matriz es regular, todas las filas tienen el mismo número de columnas.matriz[i][j]accede al elemento en la filai, columnaj.
8.3.5 Recorrer una matriz con for-each externo
Si solo vas a leer la matriz (no modificarla), puedes usar for-each para el bucle externo:
O incluso ambos con for-each:
Salida idéntica. Más limpio si solo vas a leer. Pero si necesitas el índice (por ejemplo, para modificar), usa for clásico en ambos.
8.3.6 Combinaciones: todas las parejas de dos arrays
Otro caso típico: tienes dos arrays y quieres todas las combinaciones de un elemento del primero con uno del segundo.
Salida:
3 colores × 4 tallas = 12 combinaciones. Los bucles anidados son la forma natural de generar combinaciones.
8.3.7 Sumar todos los elementos de una matriz
Patrón acumulador aplicado a matrices. Fácil: el for-each externo te da cada fila (int[]), el for-each interno te da cada valor de esa fila.
8.3.8 Complejidad de los bucles anidados
Si un bucle hace n iteraciones y el interno hace m, las combinaciones son n × m. Si n = m, son n² iteraciones. Eso crece rápido:
| n (externo) | m (interno) | Iteraciones totales |
|---|---|---|
| 10 | 10 | 100 |
| 100 | 100 | 10.000 |
| 1.000 | 1.000 | 1.000.000 |
| 10.000 | 10.000 | 100.000.000 |
Un bucle anidado con 10.000 × 10.000 = 100 millones de iteraciones ya empieza a ser lento. Por eso hay que pensar dos veces antes de meter un bucle anidado: ¿hay una forma más eficiente de hacer lo mismo? Muchas veces la hay (usando una tabla hash, por ejemplo, que verás en UD6).
8.4 La sentencia break
8.4.1 Qué hace y dónde se usa
break es la sentencia que sale inmediatamente del bucle más cercano (o del switch más cercano, como ya vimos en la sección 3). Cuando Java ejecuta break;, abandona el bucle en el que está y continúa con la instrucción siguiente al bucle.
Dónde se puede usar:
- Dentro de
whileydo-while. - Dentro de
forclásico yfor-each. - Dentro de
switch(clásico, con:). En elswitchmoderno con flecha->no hace falta, porque cada rama es exclusiva.
Dónde NO se puede usar:
- Sueltito en el medio de un método, fuera de un bucle o switch. Eso da error de compilación.
- Dentro de un
ifque no está dentro de un bucle. Elbreakse refiere al bucle envolvente, no alif.
8.4.2 Sintaxis
8.4.3 Ejemplo: buscar un elemento y salir en cuanto lo encuentres
Este es el caso de uso más clásico de break: buscar algo y parar en cuanto lo encuentras, sin esperar a recorrer todo.
Paso a paso si buscamos "Lucía":
- Iteración 1:
nombre = "Ana". No coincide. Continuamos. - Iteración 2:
nombre = "Pedro". No coincide. Continuamos. - Iteración 3:
nombre = "Lucía". Coincide.encontrado = true.break: salimos del bucle. Las iteraciones 4, 5 y 6 no se ejecutan. - Mensaje: "Sí, Lucía está en la clase."
Sin break, el bucle habría recorrido los 6 nombres aunque ya supiéramos el resultado en la posición 3. Con break, ahorramos tiempo y dejamos clara la intención: "en cuanto lo encuentres, para".
8.4.4 Diagrama de flujo con break
flowchart TD
Inicio([Inicio]) --> Init["Preparar bucle"]
Init --> Condicion{"¿Hay más\niteraciones?"}
Condicion -->|Sí| Cuerpo["Ejecutar cuerpo del bucle"]
Cuerpo --> BreakCheck{"¿Se ejecutó\nbreak?"}
BreakCheck -->|Sí| FinBreak([Salir del bucle])
BreakCheck -->|No| Condicion
Condicion -->|No| FinNormal([Fin normal])
FinBreak --> Sigue([Continúa el programa])
FinNormal --> Sigue
Hay dos formas de salir de un bucle: por la condición (fin normal) o por break (salida anticipada). En ambos casos, el programa continúa con la instrucción siguiente al bucle.
8.4.5 El patrón while (true) + break
Hay un patrón muy común: hacer un bucle "infinito" a propósito y salir con break cuando se cumpla una condición. La idea es clarificar que la salida es por una condición interna, no por la condición del while.
Salida de ejemplo:
¿Por qué while (true)? Porque la condición de salida no es una sola: el usuario puede escribir cualquier cosa, y solo una de esas cosas ("salir") debe romper el bucle. Si escribieras while (condicion), tendrías que calcular condicion antes de cada iteración, lo cual es más verboso. Con while (true) + break, la lógica de salida está dentro del bucle, donde tiene sentido.
Cuidado con los bucles infinitos de verdad
while (true) + break es legítimo solo si hay un break alcanzable dentro del bucle. Si olvidas el break, o si la condición para llegar a él nunca se cumple, el programa se queda colgado en un bucle infinito. Siempre que escribas while (true), repásate el código dos veces para asegurarte de que el break se va a alcanzar.
8.4.6 break solo sale del bucle MÁS CERCANO
Esto es importante: si tienes bucles anidados, break solo sale del bucle más cercano, no de todos los que le envuelven. Lo veremos en detalle en la sección 8.7 sobre break con etiqueta.
Salida:
El break solo corta la iteración actual de j, pero el for externo (i) continúa con su siguiente iteración. Si necesitas salir del bucle externo, tienes que usar break con etiqueta (sección 8.7).
8.5 La sentencia continue
8.5.1 Qué hace y dónde se usa
continue es la sentencia que salta a la siguiente iteración del bucle, sin ejecutar lo que queda del bloque en la iteración actual. A diferencia de break, no sale del bucle, solo pasa a la siguiente vuelta.
Diferencia con break:
| Sentencia | Sale del bucle | Pasa a la siguiente iteración |
|---|---|---|
break |
Sí, inmediatamente. | No, el bucle termina. |
continue |
No, el bucle sigue. | Sí, sin ejecutar lo que quedaba en la iteración actual. |
8.5.2 Sintaxis
8.5.3 Ejemplo: sumar solo los números positivos
Paso a paso:
n = 5: no es negativo, no entra alcontinue.suma = 0 + 5 = 5.n = -3: es negativo, entra alcontinue. Salta a la siguiente iteración.sumano se modifica.n = 8:suma = 5 + 8 = 13.n = -2: entra alcontinue. Salta.n = 7:suma = 13 + 7 = 20.n = -1: salta.n = 4:suma = 20 + 4 = 24.n = -6: salta.n = 10:suma = 24 + 10 = 34.
Salida: 34.
8.5.4 continue en for clásico: la actualización SÍ se ejecuta
En un for clásico, cuando se ejecuta continue, Java sí ejecuta el update (el i++) antes de pasar a la siguiente iteración. Vamos a verlo:
Salida:
Fíjate: el 2 no se imprime porque saltamos con continue, pero el bucle termina correctamente en i = 5. El i++ se ejecutó después del continue.
8.5.5 continue en while: la actualización NO es automática
En un while, no hay update automático. Si usas continue y la variable de control no se modifica, puedes caer en un bucle infinito:
Solución: en while, incrementa antes del continue o reorganiza la lógica para que el incremento no dependa del final del bloque.
El bucle infinito más típico de continue
Si usas while con continue, repásate SIEMPRE dónde está el incremento. Si el incremento está después del continue, ya sabes que vas a tener un bucle infinito. Es uno de los bugs más difíciles de depurar porque no da error, simplemente el programa se queda colgado.
8.5.6 continue en for-each: no hay problema
En for-each no hay índice, así que no hay riesgo de bucle infinito por continue. Simplemente, salta al siguiente elemento:
Aquí el continue es seguro: pasa al siguiente elemento sin más.
8.5.7 Cuándo usar continue (y cuándo no)
continue es útil cuando un subconjunto de iteraciones tiene un tratamiento distinto (saltárselas) y el resto sigue el flujo normal. Ejemplos buenos:
- Saltar valores negativos al sumar.
- Saltar líneas en blanco al procesar un archivo (lo verás en UD5).
- Saltar elementos que no cumplen un filtro.
Cuando NO usar continue: si lo único que hace es evitar un if, probablemente el código es más legible sin continue:
Las dos versiones hacen lo mismo. La segunda (con if invertido) es más legible para casos simples. Usa continue cuando el bloque del bucle es largo y negar la condición haría que el código quedara indentado a la derecha de más. Para casos de una sola línea, prefiere if.
8.6 return dentro de un bucle
8.6.1 return sale del método completo
return ya lo usaste en early return (sección 2). Aquí lo vemos en su contexto natural: dentro de un bucle, sale no solo del bucle, sino del método completo que lo contiene. Es la forma más radical de salir: abandona todo.
En main, return; termina el programa. Cuando veas métodos propios en UD4, return valor; termina el método y devuelve valor al llamador.
8.6.2 Ejemplo: buscar y salir con return
Aquí usamos break porque estamos en main y return; terminaría el programa sin imprimir el resultado. Pero cuando veas métodos en UD4, return será la forma natural de "encontré lo que buscaba, devuelvo el resultado y me voy":
Ese return i es más claro que un break con variable auxiliar. Cuando llegues a UD4, te acostumbrarás a usarlo.
8.6.3 return vs break vs continue
| Sentencia | Sale de... | Continúa en... |
|---|---|---|
break |
El bucle más cercano. | La instrucción siguiente al bucle. |
continue |
La iteración actual. | La siguiente iteración del bucle. |
return |
El método completo. | El llamador (donde se llamó al método). |
Regla: break y continue afectan al bucle; return afecta al método.
8.6.4 Cuándo usar return en un bucle
return es ideal cuando has encontrado lo que buscabas y ya no necesitas hacer nada más en el método:
- Buscar el índice de un elemento: cuando lo encuentras, lo devuelves.
- Comprobar que todos cumplen una condición: si encuentras uno que no, devuelves
falseinmediatamente. - Salir del
maincuando se detecta un error fatal: imprimes el error yreturn;para terminar.
Este patrón (devolver false en cuanto encuentras un contraejemplo, devolver true al final) es mucho más limpio que usar break con una bandera boolean todosOK = true. Cuando veas UD4, lo usarás mucho.
8.7 Búsqueda en matriz y break con etiqueta
8.7.1 El problema de salir de dos bucles a la vez
Vamos a buscar un valor concreto en una matriz y a salir en cuanto lo encontremos. Aquí surge la pregunta: ¿cómo salgo de los dos bucles a la vez?
Salida:
Detalles clave:
- El
breaksolo sale del bucle interno (j). - Para que el bucle externo también termine, añadimos
&& !encontradoa la condición delforexterno. Así, cuandoencontradoseatrue, la condición del externo seráfalsey terminará. - Es una técnica común: en vez de
breaketiquetado, usar una banderabooleanen la condición del bucle externo.
8.7.2 break con etiqueta: alternativa directa
Hay otra forma más directa: el break con etiqueta (labeled break).
Sintaxis:
Pasos:
- Defines una etiqueta antes del bucle externo, seguida de dos puntos:
etiqueta:. - Usas
break etiqueta;desde dentro del bucle interno (o desde dentro del externo) para salir hasta ese bucle.
Las etiquetas pueden ser cualquier nombre válido (como un identificador). Por convención, se suelen llamar outer, externo, mainLoop o algo descriptivo.
8.7.3 Ejemplo: buscar en matriz con break etiquetado
Salida:
Diferencia con la versión de bandera: aquí no necesitamos la bandera encontrado ni la condición && !encontrado. El break busqueda sale directamente de los dos bucles.
8.7.4 ¿Cuándo usar break etiquetado?
break etiquetado es poco habitual en Java. La mayoría de los casos se pueden resolver con:
- Bandera
booleanen la condición del bucle externo (vimos en 8.7.1). returndentro de un método (cuando el código está en un método, verás en UD4).- Reestructuración del código (extraer el bucle externo en un método).
Recomendaciones:
| Situación | Recomendación |
|---|---|
Buscar en matriz desde main. |
break etiquetado o bandera. |
| Buscar en matriz desde un método propio. | return con el resultado. |
| Código muy anidado (3+ niveles). | Refactorizar: extraer métodos (UD4). |
| Bucles con lógica compleja. | Replantear con otra estructura de datos (UD6). |
El goto prohibido
Quizás oigas que "el break etiquetado es como un goto". Es una exageración: goto es salto libre (a cualquier punto del programa), y está prohibido en Java. break etiquetado es salto estructurado: solo puede salir de un bucle marcado, no saltar a cualquier sitio. Aun así, muchos programadores lo evitan porque puede dificultar la lectura. Úsalo con moderación.
8.7.5 continue con etiqueta
Existe también continue etiqueta;, que salta a la siguiente iteración del bucle etiquetado (no del interno). Es aún más raro que break etiquetado. Lo mencionamos para que lo reconozcas si lo ves, pero no te recomendamos usarlo.
8.8 Cuándo los bucles anidados son legítimos (y cuándo son un code smell)
8.8.1 Casos legítimos
Los bucles anidados son legítimos y necesarios cuando:
- Recorres una estructura bidimensional (matriz, tablero, imagen).
- Generas combinaciones de dos conjuntos (parejas de elementos).
- Haces una búsqueda lineal en una matriz.
- Calculas una tabla (tabla de multiplicar, tabla de probabilidades).
- Comparas cada elemento con cada otro (pares, distancias, similitudes).
En todos estos casos, no hay forma mejor que el bucle anidado: el problema mismo es bidimensional.
8.8.2 Code smells (cuándo evitarlos)
Los bucles anidados son un mal olor (code smell) cuando:
n × mes muy grande (más de unos miles). Suele indicar que necesitas una estructura de datos mejor (hash map, set, sorted array — UD6).- El bucle interno busca en un array desordenado. Si ordenas el array o usas una tabla hash, puedes pasar de
n²anon log n. - El interno solo cuenta o comprueba algo simple. A veces se puede sustituir por una operación de Stream (UD6) o por una utilidad de
Arrays(UD6). - Hay 3 o más niveles de anidamiento. Es ilegible. Refactoriza: extrae métodos (UD4).
- Estás combinando dos colecciones por una clave común. Eso es un join, y hay estructuras específicas para ello (
Map— UD6).
8.8.3 Ejemplo: lo que NO se debe hacer
Aquí la versión con Set (UD6) es mucho más eficiente. Pero como Set se ve en UD6, por ahora el bucle anidado es la única forma que conoces. Está bien usarlo; cuando llegues a UD6, recordarás este ejemplo y verás la mejora.
8.9 Errores típicos
Antes de pasar a los ejemplos, repasamos las trampas más típicas.
8.9.1 bucle infinito por continue en while
Ya la vimos en 8.5.5: si el incremento está después del continue, nunca se ejecuta y el bucle se queda colgado.
Solución: incrementa antes del continue, o usa for clásico donde el update es automático.
8.9.2 break que solo sale del interno
Solución: para salir de los dos, usa break etiquetado o bandera en la condición del externo (ver 8.7.1 y 8.7.2).
8.9.3 return donde querías break
Solución: si solo quieres salir del bucle, usa break. Si quieres salir del método, return.
8.9.4 continue que se confunde con break
Solución: ten claro qué quieres: break sale, continue salta iteración.
8.9.5 índices mal calculados en bucles anidados
Aquí j se reinicia a 0 en cada iteración de i. El bucle while interno da 3 iteraciones por cada i, así que el resultado es correcto, pero si declaras j dentro del cuerpo del externo y la necesitas mantener entre iteraciones, pierdes su valor. Lo correcto en un for interno es declararla en el propio for:
8.9.6 complejidad oculta
Funciona, pero para n = 10.000 hace 50 millones de comparaciones. Solución: cuando veas Set (UD6), podrás hacerlo en n operaciones.
8.9.7 confundir matriz.length con matriz[i].length
Solución: matriz.length = número de filas; matriz[i].length = número de columnas de la fila i.
8.10 Ejemplos resueltos paso a paso
Cinco ejemplos completos que integran todo lo aprendido.
8.10.1 Ejemplo 1: tabla de multiplicar con formato
Salida (recortada):
Puntos clave:
- Cabecera y filas separadas: la cabecera se imprime con un solo
for(no anidado); las filas con dosforanidados. \tpara alinear columnas: no es perfecto, pero suficiente para una tabla.- Constantes
FILASyCOLUMNAS: si quieres ampliar la tabla a 20×20, cambias una línea.
8.10.2 Ejemplo 2: buscar en matriz con break etiquetado
Puntos clave:
- Etiqueta
buscar:antes delforexterno: define el punto al que apunta elbreak etiquetado. break buscar;desde dentro delforinterno: sale de los dos bucles a la vez.filaycolse inicializan a-1: si después del bucle siguen en-1, es que no se encontró.
8.10.3 Ejemplo 3: sumar solo los positivos con continue
Salida:
Puntos clave:
continuepara saltar negativos y cero (n <= 0).- Dos acumuladores:
sumapara la suma,cuentapara contar cuántos positivos hay. if (cuenta > 0)antes de la media: si no hubiera positivos, evitaríamos dividir por 0.(double) suma / cuenta: el cast adoubleasegura división decimal (si no,int/intdaint).
8.10.4 Ejemplo 4: menú interactivo con while (true) + break + continue
Puntos clave:
while (true): el bucle se repite indefinidamente.breakal escribir "salir": la única forma de salir.continueal escribir una operación no reconocida: no pide operandos, vuelve al menú.switch(de la sección 3) para ejecutar la operación.- Validación de división por cero con un
if(la verás contry-catchen la sección 8).
8.10.5 Ejemplo 5: detectar el primer duplicado en un array
Este ejemplo usa break etiquetado para salir de dos bucles a la vez, en una búsqueda de "par con valor repetido".
Salida:
Puntos clave:
- Bucles anidados con
j = i + 1: comprobamos pares(i, j)sin repetir y sin comparar un elemento consigo mismo. Es el patrón típico de "buscar duplicados" o "buscar parejas que cumplan algo". break buscaPareja;sale de los dos bucles en cuanto encuentra el primer duplicado.- Complejidad
O(n²): sinfuera muy grande (millones), habría que usar una estructura más eficiente (Setde UD6). Aquí para 10 elementos es perfectamente válido.
8.11 Buenas prácticas
8.11.1 Las 10 reglas de matrices, bucles anidados y sentencias de salto
matriz.lengthpara filas,matriz[i].lengthpara columnas. No los confundas.- Bucles anidados: solo cuando el problema es bidimensional. Si es solo "combinar dos colecciones", muchas veces hay estructuras mejores (UD6).
breakpara "ya encontré lo que buscaba". Si tu bucle es una búsqueda, usabreaken cuanto lo encuentres.continuepara "saltar esta iteración, pero seguir". Úsalo solo cuando la iteración actual no aporta nada.returnpara "ya tengo el resultado, devuélvelo". En métodos (UD4), es más limpio quebreakcon bandera.while (true) + breakes legítimo si la condición de salida no es simple. Pero asegúrate de que elbreakes alcanzable.- Evita
continueenwhilesi el incremento está después. Es la causa nº 1 de bucles infinitos. breaketiquetado como último recurso. Prefiere extraer un método (UD4) o usar bandera en la condición del externo.- No más de 3 niveles de anidamiento. Si llegas ahí, refactoriza.
- Cuidado con la complejidad
O(n²): sin > 1000, replantea.
8.11.2 Cuándo refactorizar
| Síntoma | Refactoriza a |
|---|---|
| 3+ niveles de bucles anidados. | Extrae métodos (UD4). |
while (true) con varias salidas confusas. |
Reescribe como do-while con condición clara. |
break etiquetado para salir de 3+ bucles. |
Extrae el código a un método y usa return. |
| Búsqueda lineal en array grande. | Set o Map (UD6). |
| Bucle anidado para buscar duplicados. | Set (UD6): O(n) en vez de O(n²). |
8.11.3 Comentar el break y el continue
Los break y continue son puntos donde el flujo "salta". El lector del código (incluido tú dentro de 3 meses) agradecerá un comentario que explique por qué:
Igual con continue:
8.12 Glosario rápido
| Término | Significado |
|---|---|
| Matriz bidimensional | Estructura de datos de dos dimensiones (filas × columnas), en Java int[][] o Tipo[][]. |
int[][] |
Array de arrays de int. Una matriz bidimensional. |
matriz.length |
Número de filas de la matriz. |
matriz[i].length |
Número de columnas de la fila i. |
| Bucle anidado | Bucle dentro del bloque de otro bucle. |
| Bucle externo / interno | En anidamiento, el de fuera y el de dentro respectivamente. |
break |
Sentencia que sale del bucle más cercano. |
continue |
Sentencia que salta a la siguiente iteración del bucle. |
return |
Sentencia que sale del método completo (con o sin valor). |
break etiquetado |
break etiqueta; que sale del bucle marcado con etiqueta. |
continue etiquetado |
continue etiqueta; que salta a la siguiente iteración del bucle marcado. Poco habitual. |
| Etiqueta | Identificador seguido de : que marca un bucle para break/continue etiquetado. |
| Bucle infinito | Bucle que nunca termina porque la condición nunca se hace false (o el break no se alcanza). |
while (true) + break |
Patrón de bucle con salida interna por condición. |
Complejidad O(n²) |
Cuando el algoritmo hace n × n operaciones. Crece rápido. |
| Code smell | Patrón en el código que indica un problema de diseño, aunque funcione. |
| Búsqueda lineal | Buscar elemento por elemento, sin orden ni estructura auxiliar. |
8.13 Actividades propuestas
Estas actividades tienen como objetivo integrar y aplicar los contenidos de la sección: matrices bidimensionales, bucles anidados y sentencias de salto.
Están ordenadas de menor a mayor dificultad. En las primeras se trabaja principalmente el recorrido de matrices; después se incorporan decisiones sobre el flujo de los bucles y, finalmente, se combinan varios conceptos.
Importante: salvo que el enunciado lo indique expresamente, debes decidir qué tipo de bucle y qué sentencia de salto resulta más adecuada en cada situación.
Las soluciones se publicarán en Aules.
8.13.1 Actividad 1 — Analizador de un aparcamiento
Objetivo: practicar el recorrido de matrices y el uso correcto de matriz.length y matriz[i].length.
Un aparcamiento está representado mediante una matriz de valores enteros. Cada posición representa una plaza:
0→ plaza libre.1→ plaza ocupada.2→ plaza reservada para personas con movilidad reducida.
Utiliza la siguiente matriz:
El programa debe mostrar:
- El número total de plazas libres.
- El número de plazas ocupadas.
- El número de plazas reservadas.
- La posición de todas las plazas reservadas, indicando fila y columna.
- Qué fila tiene más plazas libres.
Requisitos
- Recorre la matriz utilizando bucles anidados.
- No supongas que la matriz tiene siempre 4 filas y 6 columnas.
- Utiliza
matriz.lengthymatriz[i].length. - No es necesario modificar la matriz.
- Los resultados deben calcularse recorriendo los datos, no escribiendo los resultados directamente.
Entrega: AnalizadorAparcamiento.java.
Criterios evaluados: CE 3.2, CE 3.5, CE 3.7.
8.13.2 Actividad 2 — Control de calidad de productos
Objetivo: practicar continue y utilizarlo para controlar qué elementos de una secuencia deben procesarse.
Una fábrica registra mediante un array las mediciones de temperatura de una máquina durante un turno:
El valor -1 significa que no se pudo realizar la medición y, por tanto, no debe participar en los cálculos.
El programa debe calcular:
- Número de mediciones válidas.
- Temperatura media de las mediciones válidas.
- Temperatura máxima.
- Temperatura mínima.
- Número de mediciones que están fuera del intervalo recomendado
[20.0, 25.0].
Requisitos
- Las mediciones con valor
-1deben ignorarse. - Debes utilizar
continuepara saltar las mediciones que no sean válidas. - No debes contar las mediciones descartadas como mediciones realizadas.
- Si no existe ninguna medición válida, el programa debe mostrar un mensaje apropiado y no intentar calcular una media.
Reflexión
Explica brevemente por qué en este caso continue resulta más apropiado que break.
Entrega: ControlCalidad.java.
Criterios evaluados: CE 3.2, CE 3.3, CE 3.5, CE 3.7.
8.13.3 Actividad 3 — Ruta de un robot
Objetivo: combinar matrices, bucles anidados y break para controlar una búsqueda.
Un robot se mueve por una cuadrícula representada por una matriz:
Los caracteres representan:
'.'→ posición transitable.'#'→ obstáculo.'S'→ posición de salida.
El programa debe localizar la salida y mostrar sus coordenadas.
Sin embargo, existe una condición adicional: el robot comienza a explorar desde la esquina superior izquierda y las posiciones se analizan de izquierda a derecha y de arriba abajo.
Cuando se encuentre la salida, no tiene sentido continuar recorriendo el resto del mapa.
Requisitos
- Utiliza dos bucles anidados.
- El recorrido debe realizarse en el orden indicado.
- Cuando encuentres
'S', debes abandonar la búsqueda. - No debes seguir recorriendo posiciones después de encontrarla.
- Si no existiera salida, debe mostrarse un mensaje indicándolo.
Parte adicional
Modifica el programa para contar cuántas posiciones transitables ('.') se han analizado antes de encontrar la salida.
Pregunta para razonar: ¿es suficiente un break normal para resolver el problema? Justifica tu respuesta.
Entrega: RutaRobot.java.
Criterios evaluados: CE 3.2, CE 3.3, CE 3.5, CE 3.7.
8.13.4 Actividad 4 — Detectar una situación de emergencia
Objetivo: decidir entre break, continue y return según el efecto que se desea conseguir.
Un centro de control recibe datos de diferentes sensores:
Cada valor representa una medición.
- Los valores
-1representan una lectura incorrecta y deben ignorarse. - Los valores entre
0y50son normales. - Un valor superior a
50representa una situación de emergencia.
El programa debe analizar las mediciones.
Si encuentra una lectura incorrecta, debe ignorarla y continuar con la siguiente.
Si encuentra una emergencia:
- Debe mostrar su posición.
- Debe mostrar el valor detectado.
- Debe dejar de analizar las mediciones.
- Debe indicar cuántas mediciones válidas se habían procesado antes de la emergencia.
Requisitos
El alumno debe decidir cómo implementar la salida de los bucles.
Se permite utilizar:
- una variable de control,
breaketiquetado,- o cualquier otra solución basada exclusivamente en los contenidos de esta sección.
No se debe utilizar return simplemente porque resulte más corto: la solución debe justificar qué parte del programa se pretende abandonar.
Parte de análisis
Entrega también una breve explicación respondiendo:
- ¿Dónde utilizarías
continue? - ¿Dónde utilizarías
break? - ¿Sería apropiado utilizar
return? ¿Por qué? - ¿Qué solución consideras más legible?
Entrega: ControlEmergencia.java.
Criterios evaluados: CE 3.2, CE 3.3, CE 3.5, CE 3.7.
8.13.5 Actividad 5 — Simulador de inventario de un almacén
Objetivo: integrar todos los contenidos de la sección en un problema de mayor complejidad y tomar decisiones sobre el control del flujo.
Un almacén está dividido en zonas. Cada zona contiene varias posiciones y se representa mediante una matriz:
Cada número indica las unidades almacenadas en una posición. El valor 0 significa que la posición está vacía.
El programa debe ofrecer el siguiente menú:
Para la opción 4, el programa considerará que cada posición contiene un número de unidades y el usuario introducirá una cantidad que desea localizar. Debe indicar si existe alguna posición que contenga exactamente esa cantidad y mostrar la primera posición encontrada.
Para la opción 5, el usuario introducirá una cantidad máxima permitida por posición. El programa debe comprobar si existe alguna posición que supere ese límite.
Requisitos
El programa debe:
- Utilizar una matriz bidimensional.
- Recorrerla mediante bucles anidados.
- Utilizar
while (true)para gestionar el menú. - Permitir abandonar el menú mediante una sentencia de salto apropiada.
- Ignorar las posiciones vacías cuando corresponda.
- Detener una búsqueda cuando ya no sea necesario continuar.
- Evitar recorrer más elementos de los necesarios.
- Mostrar mensajes claros al usuario.
- Comentar las decisiones de flujo especialmente cuando se utilice
breakocontinue.
Restricción importante
No se indica qué sentencia (break, continue o return) debes utilizar en cada parte.
Debes escogerla en función de lo que quieras conseguir y justificar al menos dos de tus decisiones.
Ampliación opcional
Añade una opción:
que permita seleccionar una posición de origen y otra de destino, trasladando las unidades almacenadas.
Debes controlar que:
- Las posiciones estén dentro de los límites de la matriz.
- La posición de origen no esté vacía.
- La posición de destino pueda recibir la mercancía.
Entrega: ControlAlmacen.java.
Criterios evaluados: CE 3.2, CE 3.3, CE 3.5, CE 3.7.
8.13.6 Actividad 6 — Análisis de código: ¿qué hace realmente?
Objetivo: comprobar que se comprende el comportamiento de los bucles anidados y las sentencias de salto, y no únicamente su sintaxis.
Analiza el siguiente código sin ejecutarlo inicialmente:
Responde:
- ¿Qué valores visita el programa?
- ¿En qué momento se ejecuta
continue? - ¿Qué efecto tiene?
- ¿En qué momento se ejecuta
break externo? - ¿Qué bucles se abandonan?
- ¿Qué valor termina teniendo
contador? - ¿Qué ocurriría si
break externose sustituyera porbreak? - Modifica el código para que el
9no termine la búsqueda, sino que simplemente se ignore. - Modifica nuevamente el código para que al encontrar el
9termine el método completo.
Finalmente, ejecuta el programa y comprueba tus respuestas.
Entrega: AnalisisFlujo.md.
Criterios evaluados: CE 3.2, CE 3.3, CE 3.7.
Cierre de la sección. Llegar al final del bloque de iteración es un hito. Tienes en el cinturón cinco herramientas para repetir (
while,do-while,for,for-eachy la combinación conbreak/continue/return), más las matrices bidimensionales para problemas con filas y columnas, y ya sabes decidir cuándo usar cada una. Los bucles anidados, aunque parecen una cosa menor, son la base de muchísimos algoritmos: ordenación, búsqueda, procesamiento de imágenes, juegos de tablero... Y elbreaketiquetado, aunque poco frecuente, te resuelve problemas que de otra forma requerían refactorizar. La regla más importante que te llevas de esta sección es no abusar: los bucles anidados son potentes, peroO(n²)crece rápido. Cuando llegues a UD6 y conozcasSetyMap, vas a poder hacer conO(n)lo que ahora haces conO(n²). Esa es la belleza de saber más estructuras de datos: cada problema tiene SU herramienta. Nos vemos en la sección 9, donde por fin vamos a aprender a manejar errores en vez de dejar que el programa se caiga.