Saltar a contenido

5. Arrays unidimensionales

UD2 — 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.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 ~3 h (de las 30 h de la UD2).

Requisitos previos (lo que ya sabes): condiciones booleanas y operadores lógicos (sección 1 de UD2); if / else if / else, early return, validación con IO.readln; switch expression con flecha y múltiples etiquetas (sección 3 de UD2); while y do-while (sección 4 de UD2). Tipos primitivos, var, String (con .equals(), .equalsIgnoreCase(), .length(), .trim(), .toLowerCase(), .charAt()), text blocks, final para constantes y operador ternario (de UD1). Método estático como Math.sqrt() o Integer.parseInt() (de UD1): aquí verás más métodos estáticos, los de java.util.Arrays.


5.1 ¿De qué va esta sección? — Un solo nombre para muchos datos

Hasta ahora, todas tus variables eran simples: una int edad, un String nombre, un double precio. Cada variable contenía un solo valor. Eso está bien para muchos programas, pero hay un momento en que no basta.

Imagina que quieres guardar las 5 notas de un alumno, los 7 días de la semana, o los 30 alumnos de una clase. Si tuvieras que declarar nota1, nota2, nota3, nota4, nota5... te volverías loco. Y peor aún: ¿qué pasa si son 100 notas? ¿Y si el número lo decide el usuario en tiempo de ejecución?

Para eso existen los arrays (también llamados "arreglos" o "vectores" en español): un tipo compuesto que permite guardar varios valores del mismo tipo bajo un solo nombre, accesibles por su posición. Java los tiene desde la versión 1.0, son el tipo compuesto más básico del lenguaje, y son la base sobre la que se construyen todas las colecciones modernas (que verás en UD6).

En esta sección vas a aprender el tipo array en sí: cómo se declara, cómo se inicializa y cómo se accede a sus elementos. No verás todavía cómo recorrerlos con un bucle: eso es contenido de la sección 6, donde se introduce el for clásico, la herramienta natural para recorrer arrays. Aquí nos limitamos a la estructura de datos: crearla, llenarla y consultar elementos sueltos.

Cubriremos, en este orden:

  1. Qué es un array y sus tres propiedades clave.
  2. Declaración e inicialización: con valores literales y con tamaño fijo.
  3. .length y acceso por índice (lectura y escritura).
  4. Valores por defecto en arrays.
  5. Tipos de arrays: int[], double[], String[], char[], boolean[].
  6. String.toCharArray() como puente entre String y char[].
  7. Operaciones básicas con elementos sueltos (sin recorrido): lectura, escritura, swap.
  8. Pequeños recorridos con while (el bucle que ya conoces de la sección 4) para inicializar arrays pidiendo datos al usuario.
  9. Trampas clásicas con arrays.
  10. Ejemplos resueltos paso a paso.

Analogía del archivador

Piensa en una variable simple como una carpeta con un folio dentro: solo cabe un dato. Un array es como un archivador con varias carpetas etiquetadas por número: carpeta 0, carpeta 1, carpeta 2... Cada carpeta contiene un dato, pero todas comparten el mismo "apellido" (el nombre del array). Para referirte a un dato, dices el nombre del archivador y el número de carpeta: notas[3] es "el dato de la carpeta 3 del archivador notas".

Vamos a empezar por definir qué es un array con precisión.


5.2 ¿Qué es un array? Sus tres propiedades clave

Un array es una secuencia fija de elementos del mismo tipo, almacenados contiguamente en memoria y accesibles por un índice numérico que empieza en 0. De esa definición se desprenden tres propiedades clave que vas a recordar toda tu vida como programador:

5.2.1 Propiedad 1: tamaño fijo

Una vez creado un array, no se puede cambiar su tamaño. Si declaras int[] nums = new int[5], ese array tendrá 5 elementos para siempre: no se puede ampliar a 6 ni reducir a 4. Si necesitas más tamaño, tienes que crear un array nuevo y copiar los elementos (lo verás en la sección 6 con Arrays.copyOf).

Si necesitas una estructura que crezca o encoja dinámicamente, espera a UD6: las colecciones (ArrayList, LinkedList) hacen exactamente eso. Pero los arrays son fijos.

5.2.2 Propiedad 2: mismo tipo

Todos los elementos de un array son del mismo tipo. Si declaras int[] nums, los 5 elementos son int. No puedes mezclar: no puedes tener nums[0] = 5 y nums[1] = "hola". El compilador te da error.

Si necesitas mezclar tipos, normalmente no es un array lo que necesitas: es una clase (lo verás en UD4) o un Object[] con pattern matching (UD7).

5.2.3 Propiedad 3: índice desde 0

Los elementos se acceden por un índice numérico que empieza en 0. El primer elemento es arr[0], el segundo es arr[1], el tercero es arr[2], etc. El último es arr[arr.length - 1].

Use mouse to pan and zoom
flowchart LR
    subgraph Array ["int[] nums = {10, 20, 30, 40, 50}"]
        P0["nums[0] = 10"]
        P1["nums[1] = 20"]
        P2["nums[2] = 30"]
        P3["nums[3] = 40"]
        P4["nums[4] = 50"]
    end

Esta convención "índice desde 0" es universal en programación (no solo en Java: C, C++, Python, JavaScript, C#, todos la usan). ¿Por qué? Porque el índice es en realidad un desplazamiento desde el inicio del array: arr[0] está a 0 posiciones del inicio, arr[1] a 1 posición, arr[2] a 2 posiciones... Es una decisión histórica que viene del lenguaje C y que se ha mantenido por eficiencia.

Cuidado con el último índice

El último índice válido de un array de 5 elementos es 4, no 5. Intentar acceder a nums[5] lanza ArrayIndexOutOfBoundsException. Verás cómo manejar esa excepción en la sección 9; por ahora, simplemente recuerda: índices válidos van de 0 a array.length - 1.

5.2.4 Representación visual

Un array de 5 enteros se suele representar así:

1
2
3
4
┌────┬────┬────┬────┬────┐
│ 10 │ 20 │ 30 │ 40 │ 50 │   ← valores
└────┴────┴────┴────┴────┘
   0    1    2    3    4     ← índices
  • La fila de arriba son los valores almacenados.
  • La fila de abajo son los índices (de 0 a 4).
  • El array tiene length = 5 (5 elementos).

Memoriza esta imagen: cuando pienses en un array, dibújalo mentalmente así.


5.3 Declaración e inicialización

5.3.1 Forma 1: con valores literales (la más simple)

La forma más simple de crear un array es declararlo con los valores entre llaves:

1
2
3
4
5
int[] primos = {2, 3, 5, 7, 11};
double[] precios = {1.50, 2.20, 0.99, 5.00};
String[] nombres = {"Ana", "Juan", "María", "Pedro"};
char[] letras = {'a', 'b', 'c', 'd', 'e'};
boolean[] flags = {true, false, true, true};

Sintaxis:

Tipo[] nombre = { valor1, valor2, valor3, ... };
  • Tipo[] indica "array de Tipo". Los corchetes pueden ir después del tipo o después del nombre (Tipo nombre[]), pero la convención en Java es ponerlos después del tipo: int[] nums, no int nums[].
  • { ... } contiene los valores separados por comas. Java cuenta cuántos hay y crea el array con ese tamaño automáticamente.

5.3.2 Forma 2: con tamaño fijo (sin valores iniciales)

Si no sabes los valores iniciales (porque los vas a pedir al usuario o a calcular), declaras el array con un tamaño fijo usando new:

1
2
3
4
int[] edades = new int[5];            // 5 enteros, todos 0 por defecto
double[] temperaturas = new double[7];   // 7 doubles, todos 0.0
String[] palabras = new String[10];      // 10 Strings, todos null por defecto
boolean[] flags = new boolean[3];        // 3 booleans, todos false

Sintaxis:

Tipo[] nombre = new Tipo[tamaño];
  • new Tipo[tamaño] reserva espacio para tamaño elementos.
  • Los elementos se inicializan al valor por defecto del tipo (ver sección 5.5).
  • Después puedes asignar valores a cada posición: edades[0] = 25; edades[1] = 30; ....

5.3.3 Forma 3: declarar y luego inicializar (en dos pasos)

También puedes declarar primero y asignar después, pero hay un detalle: cuando asignas después, la sintaxis con { } ya no funciona directamente; tienes que usar new:

1
2
3
4
5
6
7
8
9
int[] nums;                  // declarar (sin inicializar)
nums = new int[5];           // inicializar con tamaño
nums[0] = 10;                // asignar valores uno a uno
nums[1] = 20;
// ...

// Y en una sola línea con valores:
int[] otros;                 
otros = new int[]{1, 2, 3};   // OJO: hay que poner 'new int[]' antes de las llaves

Esta tercera forma es menos habitual y la verás sobre todo cuando un método devuelve un array. Por ahora, quédate con las dos primeras: declarar con valores literales ({ ... }) si los conoces, o con new Tipo[n] si no.

5.3.4 ¿Cuándo usar cada forma?

Situación Forma recomendada
Conoces los valores iniciales (constantes). Tipo[] arr = { v1, v2, v3 };
No los conoces, pero sabes cuántos necesitas. Tipo[] arr = new Tipo[n]; + asignación posterior
No los conoces y el tamaño depende de cálculos. Calcula el tamaño, luego new Tipo[tamaño];
El método recibe el array como parámetro y solo lo declara. Tipo[] arr; (declarar sin inicializar)

En la mayoría de ejercicios de esta sección usaremos la forma 1 (con valores literales) para que el código sea claro. La forma 2 la usarás cuando pidas datos al usuario: preguntas cuántos elementos, creas el array con new Tipo[n], y vas rellenando (en la sección 5.8 verás cómo hacerlo con un while).


5.4 .length y acceso por índice

5.4.1 .length: cuántos elementos tiene

Cada array tiene un campo llamado .length (¡sin paréntesis!) que indica cuántos elementos contiene:

int[] primos = {2, 3, 5, 7, 11};
int n = primos.length;   // 5

.length es un campo, no un método. Por eso no lleva paréntesis. Esta es la diferencia más comúnmente confundida con String.length(), que sí lleva paréntesis porque es un método.

Tipo Sintaxis Paréntesis
Array arr.length Sin paréntesis (es un campo público).
String s.length() Con paréntesis (es un método).

Memoriza esta diferencia: array → length, String → length(). Te la vas a encontrar miles de veces.

5.4.2 Acceso por índice: lectura y escritura

Para leer o escribir un elemento concreto, usa array[indice]:

1
2
3
4
5
6
7
8
9
int[] primos = {2, 3, 5, 7, 11};

// Lectura
int primero = primos[0];   // 2
int tercero = primos[2];   // 5
int ultimo = primos[primos.length - 1];   // 11 (truco para el último)

// Escritura
primos[0] = 13;            // ahora primos = {13, 3, 5, 7, 11}

Detalles:

  1. primos[0] accede al primer elemento (índice 0).
  2. primos.length - 1 es el truco estándar para el último elemento. Memorízalo.
  3. primos[0] = 13 es escritura: modifica el valor de esa posición.

5.4.3 Trampas con el índice

1
2
3
4
int[] primos = {2, 3, 5, 7, 11};

int x = primos[5];   // ¡ArrayIndexOutOfBoundsException! (solo hay 0-4)
int y = primos[-1];  // ¡También ArrayIndexOutOfBoundsException!

Los índices válidos van de 0 a array.length - 1. Cualquier índice fuera de ese rango lanza ArrayIndexOutOfBoundsException (una excepción que verás cómo manejar en la sección 9; por ahora, si la ves, es que te has salido del rango).

Por qué el índice máximo es length - 1

Si el array tiene 5 elementos, sus índices son 0, 1, 2, 3, 4 (cinco índices). El máximo índice es 4, que es length - 1 = 5 - 1 = 4. Es una aritmética simple, pero fácil de confundir cuando vas rápido.

5.4.4 Modificar un elemento concreto

1
2
3
int[] nums = {1, 2, 3, 4, 5};

nums[2] = 99;   // ahora nums = {1, 2, 99, 4, 5}

Modificar un elemento suelto no requiere ningún bucle: solo accedes por índice y asignas. Es la operación más básica con arrays.

5.4.5 Swap: intercambiar dos elementos

El swap (intercambiar dos valores) es un patrón clásico que usa una variable temp para no perder el valor:

1
2
3
4
5
6
7
int[] arr = {10, 20, 30, 40, 50};

// Intercambiar el primero y el último
int temp = arr[0];
arr[0] = arr[4];
arr[4] = temp;
// ahora arr = {50, 20, 30, 40, 10}

Paso a paso del swap:

  1. temp = arr[0] guarda el valor original de arr[0] (10).
  2. arr[0] = arr[4] sobrescribe arr[0] con el valor de arr[4] (50).
  3. arr[4] = temp pone en arr[4] el valor original (10).

Sin temp, al hacer arr[0] = arr[4] perderías el valor original de arr[0] y no podrías usarlo después. Lo verás mucho en algoritmos de ordenación (UD6).

5.4.6 Acceso por índice calculado

El índice puede ser cualquier expresión que devuelva un int, no solo un literal:

1
2
3
4
5
6
7
8
9
int[] nums = {10, 20, 30, 40, 50};

int i = 2;
IO.println(nums[i]);              // 30 (i es una variable)
IO.println(nums[i + 1]);          // 40 (expresión)
IO.println(nums[nums.length - 1]); // 50 (último)

int j = Integer.parseInt(IO.readln().trim());
IO.println(nums[j]);             // El índice lo decide el usuario

Esto te da mucha flexibilidad: puedes pedir al usuario la posición que quiere consultar, calcular índices con expresiones, etc.


5.5 Valores por defecto en arrays

Cuando creas un array con new Tipo[n], Java inicializa los elementos al valor por defecto del tipo:

Tipo de elementos Valor por defecto
int, long, short, byte 0
double, float 0.0
boolean false
char '\u0000' (carácter nulo, invisible)
String y cualquier objeto null

5.5.1 Ejemplo práctico

int[] edades = new int[5];
// edades = {0, 0, 0, 0, 0}

double[] temps = new double[3];
// temps = {0.0, 0.0, 0.0}

boolean[] flags = new boolean[4];
// flags = {false, false, false, false}

String[] nombres = new String[3];
// nombres = {null, null, null}

Esto es útil porque significa que no tienes que inicializar todos los elementos a mano: el array ya viene "preparado" con valores razonables para tipos primitivos.

5.5.2 Trampa con null en arrays de String

1
2
3
String[] nombres = new String[3];   // {null, null, null}

int n = nombres[0].length();   // ¡NullPointerException!

Si creas String[] palabras = new String[5], los 5 elementos son null, no "". Llamar a palabras[0].length() lanza NullPointerException porque estás llamando a un método sobre null.

Solución: si necesitas strings vacíos en vez de null, los inicializas uno a uno (verás cómo hacerlo con un while en la sección 5.8) o declaras con valores literales desde el principio si los conoces.

5.5.3 Diferencia entre array vacío y array null

Hay dos formas distintas de "no tener elementos":

int[] vacio = {};      // Array con 0 elementos. .length = 0.
int[] nulo = null;     // No es un array, es "ausencia de array".
  • Un array vacío ({} o new int[0]) es un array legítimo con 0 elementos. Si accedes a vacio.length te da 0. No da error (aunque obviamente no puedes acceder a vacio[0]).
  • Un array null no es un array: es una variable que apunta a "nada". Intentar acceder a nulo.length o a nulo[0] lanza NullPointerException.

Esta distinción es importante: si recibes un array como parámetro y puede ser null, comprueba antes de usarlo:

1
2
3
4
5
if (nulo != null) {
    int n = nulo.length;   // OK
} else {
    IO.println("El array era null");
}

5.6 Tipos de arrays

Puedes declarar arrays de cualquier tipo. Veamos los más comunes.

5.6.1 Arrays de tipos primitivos

1
2
3
4
5
int[] enteros = {1, 2, 3, 4, 5};
double[] decimales = {1.5, 2.5, 3.14, 9.81};
boolean[] flags = {true, false, true};
char[] letras = {'a', 'b', 'c', 'd'};
long[] grandes = {1_000_000L, 2_000_000L, 3_000_000L};

Cada tipo primitivo tiene su propia sintaxis de array. Los valores literales de cada tipo siguen las mismas reglas que ya viste en UD1: sufijo L para long, sufijo f para float, etc.

5.6.2 Array de String

String[] nombres = {"Ana", "Juan", "María", "Pedro"};
String[] dias = {"Lunes", "Martes", "Miércoles", "Jueves", "Viernes", "Sábado", "Domingo"};

Un String[] es un array de referencias a objetos String. Cada elemento es una referencia (no el objeto directamente). Esto significa que:

  • Si no inicializas el array con new, los elementos son null (ver 5.5.2).
  • Los elementos String son inmutables: no puedes cambiar el contenido de un String, pero sí reemplazarlo por otro (arr[i] = arr[i].toUpperCase();).

5.6.3 Array de char

1
2
3
4
char[] letras = {'a', 'b', 'c', 'd', 'e'};

IO.println(letras[0]);   // 'a'
IO.println(letras[4]);   // 'e'

Los arrays de char son especialmente útiles porque son la "materia prima" para procesar texto carácter a carácter (ver 5.7).

5.6.4 Tabla resumen

Tipo Sintaxis Ejemplo
int int[] {1, 2, 3}
double double[] {1.5, 2.5}
boolean boolean[] {true, false}
char char[] {'a', 'b'}
String String[] {"Ana", "Juan"}
Cualquier otro Tipo[] Depende del tipo

La regla general: pon corchetes después del tipo para indicar que es un array.


5.7 String.toCharArray(): el puente entre String y char[]

Hay un caso especial muy útil: cuando quieres trabajar con los caracteres de un String como si fueran un array. Aquí surge un pequeño problema:

String no es un array de caracteres desde el punto de vista de Java. No puedes escribir texto[0] para obtener el primer carácter de un String; tienes que usar el método charAt(0):

1
2
3
4
String texto = "hola";
char primero = texto.charAt(0);   // 'h'
char segundo = texto.charAt(1);   // 'o'
int n = texto.length();           // 4 (con paréntesis: es método)

Y para convertir un String en un array de caracteres, usas toCharArray():

1
2
3
4
5
6
String texto = "hola";
char[] letras = texto.toCharArray();   // {'h', 'o', 'l', 'a'}

// Ahora 'letras' es un array normal, con .length (sin paréntesis) y arr[i]
int n = letras.length;   // 4
char primero = letras[0]; // 'h'

5.7.1 ¿Por qué usar toCharArray()?

Porque los arrays de char son más cómodos de manejar cuando quieres:

  • Acceder a caracteres por índice (letras[i]).
  • Modificar caracteres (los String son inmutables; los char[] no).
  • Aplicar algoritmos de búsqueda o conteo sobre los caracteres (en la sección 6 verás cómo recorrerlos con for).

5.7.2 Ejemplo: consultar caracteres sueltos

1
2
3
4
5
6
7
8
9
String texto = "Programacion";

char[] letras = texto.toCharArray();

// Consultas sueltas (sin recorrer el array completo)
IO.println("Primera letra: " + letras[0]);              // 'P'
IO.println("Última letra: " + letras[letras.length - 1]); // 'n'
IO.println("Letra del medio: " + letras[letras.length / 2]); // 'a' (aprox.)
IO.println("Total de letras: " + letras.length);          // 11

Aquí accedemos a elementos sueltos sin recorrer todo el array. Cuando veas el for clásico en la sección 6, podrás pasar por todos ellos en bucle.

5.7.3 charAt(i) como alternativa sin crear array

1
2
3
4
5
String texto = "hola";

char primero = texto.charAt(0);   // 'h'
char segundo = texto.charAt(1);   // 'o'
int n = texto.length();           // 4

charAt(i) te devuelve el carácter en la posición i sin crear un array nuevo. Es más eficiente en memoria. En la sección 6 verás cómo combinar charAt con un for para recorrer el String directamente sin convertirlo.


5.8 Pequeños recorridos con while

Aunque el recorrido completo de arrays se ve en la sección 6 con for clásico, hay un caso donde necesitas un bucle ahora mismo: cuando pides datos al usuario para rellenar un array. Vamos a verlo con while (el bucle que ya conoces de la sección 4).

5.8.1 Patrón: pedir N datos y guardarlos en un array

int NUM_ALUMNOS = 5;
String[] nombres = new String[NUM_ALUMNOS];

int i = 0;
while (i < nombres.length) {
    IO.print("Nombre del alumno " + (i + 1) + ": ");
    nombres[i] = IO.readln().trim();
    i++;
}

// Ahora el array está relleno. Mostramos el primero y el último como prueba.
IO.println("Primer alumno: " + nombres[0]);
IO.println("Último alumno: " + nombres[nombres.length - 1]);

Salida de ejemplo:

1
2
3
4
5
6
7
Nombre del alumno 1: Ana
Nombre del alumno 2: Juan
Nombre del alumno 3: María
Nombre del alumno 4: Pedro
Nombre del alumno 5: Lucía
Primer alumno: Ana
Último alumno: Lucía

Detalles importantes:

  1. int i = 0 se declara fuera del while porque el while no tiene inicialización propia (a diferencia del for, que verás en la sección 6).
  2. i < nombres.length es la condición: seguimos mientras i sea menor que el tamaño del array. Es <, no <= (la trampa nº 1 con arrays, ya la verás en la sección 6).
  3. nombres[i] = IO.readln().trim() guarda la entrada del usuario en la posición i del array.
  4. i++ al final del cuerpo: obligatorio. Si lo olvidas, el bucle se queda colgado en i = 0 para siempre (bucle infinito).
  5. i + 1 al mostrar el mensaje: como los índices empiezan en 0, sumamos 1 para que el usuario vea "1, 2, 3..." (no "0, 1, 2...").

5.8.2 Ejemplo: pedir 5 edades con while

public class PedirEdades {

    public static void main(String[] args) {

        final int NUM_EDADES = 5;
        int[] edades = new int[NUM_EDADES];

        // Pedir las edades con while
        int i = 0;
        while (i < edades.length) {
            IO.print("Edad " + (i + 1) + ": ");
            String entrada = IO.readln().trim();
            edades[i] = Integer.parseInt(entrada);
            i++;   
        }

        // Mostrar primero y último
        IO.println("Primera edad: " + edades[0]);
        IO.println("Última edad: " + edades[edades.length - 1]);

        // Calcular suma con while
        int suma = 0;
        int j = 0;
        while (j < edades.length) {
            suma = suma + edades[j];
            j++;
        }
        IO.println("Suma: " + suma);
    }
}

Salida de ejemplo:

Edad 1: 18
Edad 2: 21
Edad 3: abc
Error: debes escribir un número entero positivo.
Edad 3: 19
Edad 4: 22
Edad 5: 20
Primera edad: 18
Última edad: 20
Suma: 100

Puntos clave:

  1. Dos while distintos: uno para pedir datos, otro para sumarlos. Cada uno tiene su propio índice (i y j). En la sección 6 verás cómo el for clásico simplifica esto.
  2. edades[0] y edades[edades.length - 1] para acceder al primero y al último sin recorrer todo el array.

5.9 Errores típicos con arrays

Antes de pasar a los ejemplos, repasamos las trampas más típicas.

5.9.1 length con paréntesis

1
2
3
int[] arr = {1, 2, 3};
int n = arr.length();   // ERROR: length es un campo, no un método
int n = arr.length;     // CORRECTO

Solución: memoriza array → length, String → length().

5.9.2 índice fuera de rango

1
2
3
int[] arr = {1, 2, 3};
int x = arr[3];   // ArrayIndexOutOfBoundsException (índices válidos: 0, 1, 2)
int y = arr[-1];  // También ArrayIndexOutOfBoundsException

Solución: los índices válidos van de 0 a array.length - 1. Antes de acceder a arr[i], asegúrate de que i está en ese rango.

5.9.3 array null vs array vacío

int[] vacio = {};      // Array con 0 elementos. .length = 0.
int[] nulo = null;     // No es un array, es "ausencia de array".

Si accedes a nulo.length o nulo[0], se lanza NullPointerException. Antes de usar un array que puede ser null, comprueba:

1
2
3
if (nulo != null) {
    int n = nulo.length;
}

5.9.4 null en arrays de String

1
2
3
String[] palabras = new String[3];   // {null, null, null}

int n = palabras[0].length();   // NullPointerException

Los arrays de String inicializados con new tienen null en cada posición, no "". Antes de usar un elemento, comprueba o inicializa a "".

5.9.5 olvidar i++ en el while al recorrer un array

1
2
3
4
5
6
int[] arr = {1, 2, 3, 4, 5};
int i = 0;
while (i < arr.length) {
    IO.println(arr[i]);   // imprime 1 infinitamente: i nunca cambia
    // ¡Falta i++!
}

Solución: siempre que uses while con un índice, asegúrate de incrementar el índice en algún punto del cuerpo. Si no, bucle infinito. Esta trampa desaparece con el for clásico de la sección 6, porque la actualización va en la propia cabecera del for y no puedes olvidarla.

5.9.6 intentar imprimir un array directamente

int[] arr = {1, 2, 3};
IO.println(arr);   // imprime algo como "[I@1b6d3586" (no los contenidos)

Si imprimes un array con IO.println(arr), verás algo como [I@1b6d3586 (para int[]) o [Ljava.lang.String;@1540e19d (para String[]). Eso es la representación por defecto del objeto array, no sus contenidos.

Solución temporal (sección 5): imprime los elementos sueltos:

IO.println(arr[0] + ", " + arr[1] + ", " + arr[2]);   // 1, 2, 3

Solución definitiva (sección 6): Arrays.toString(arr) te da [1, 2, 3] en una sola línea. La verás en la sección 6 con las utilidades de java.util.Arrays.

5.10 Ejemplos resueltos paso a paso

Cinco ejemplos completos que integran todo lo aprendido.

5.10.1 Ejemplo 1: crear y consultar un array de Strings

public class CrearArrayStrings {

    public static void main(String[] args) {

        // Creación con valores literales
        String[] dias = {"Lunes", "Martes", "Miércoles", "Jueves",
                         "Viernes", "Sábado", "Domingo"};

        // Consultas sueltas (sin recorrido)
        IO.println("Primer día: " + dias[0]);
        IO.println("Último día: " + dias[dias.length - 1]);
        IO.println("Día del medio: " + dias[dias.length / 2]);
        IO.println("Total de días: " + dias.length);

        // Modificar un elemento suelto
        dias[0] = "LUNES";
        IO.println("Primer día modificado: " + dias[0]);
    }
}

Salida:

1
2
3
4
5
Primer día: Lunes
Último día: Domingo
Día del medio: Jueves
Total de días: 7
Primer día modificado: LUNES

Puntos clave:

  1. Creación con { }: 7 valores literales, sin new.
  2. Acceso suelto por índice: dias[0], dias[length - 1], dias[length / 2].
  3. Modificación de un elemento: dias[0] = "LUNES".
  4. Sin recorrer el array: solo consultas sueltas. Para recorrerlo entero, esperamos a la sección 6.

5.10.2 Ejemplo 2: pedir 5 edades con while y mostrar la suma

public class PedirEdadesYSumar {

    public static void main(String[] args) {

        final int NUM_EDADES = 5;
        int[] edades = new int[NUM_EDADES];

        // 1. Pedir las edades con while
        int i = 0;
        while (i < edades.length) {
            IO.print("Edad " + (i + 1) + ": ");
            String entrada = IO.readln().trim();
            edades[i] = Integer.parseInt(entrada);
            i++;
        }

        // 2. Calcular la suma con while
        int suma = 0;
        int j = 0;
        while (j < edades.length) {
            suma = suma + edades[j];
            j++;
        }

        // 3. Mostrar resultados
        double media = (double) suma / edades.length;
        IO.println("Suma total: " + suma);
        IO.println(String.format("Media: %.2f", media));
        IO.println("Primera edad: " + edades[0]);
        IO.println("Última edad: " + edades[edades.length - 1]);
    }
}

Salida de ejemplo:

1
2
3
4
5
6
7
8
9
Edad 1: 18
Edad 2: 21
Edad 3: 19
Edad 4: 22
Edad 5: 20
Suma total: 100
Media: 20.00
Primera edad: 18
Última edad: 20

Puntos clave:

  1. new int[NUM_EDADES] crea el array con tamaño fijo, todos a 0 por defecto.
  2. Dos while distintos para dos recorridos distintos: uno para pedir, otro para sumar. Cada uno con su propio índice (i y j).
  3. (double) suma / edades.length cast a double para que la división sea decimal.
  4. Acceso al primero y al último sin recorrer: edades[0] y edades[edades.length - 1].

5.10.3 Ejemplo 3: análisis de caracteres con toCharArray

public class AnalisisCaracteres {

    public static void main(String[] args) {

        IO.print("Escribe una palabra: ");
        String texto = IO.readln().trim();

        // Convertir a char[]
        char[] letras = texto.toCharArray();

        // Consultas sueltas sin recorrer
        IO.println("Total de caracteres: " + letras.length);
        IO.println("Primera letra: " + letras[0]);
        IO.println("Última letra: " + letras[letras.length - 1]);
        IO.println("Letra del medio: " + letras[letras.length / 2]);

        // Contar vocales con while
        int vocales = 0;
        int i = 0;
        while (i < letras.length) {
            char l = Character.toLowerCase(letras[i]);
            boolean esVocal =  l == 'a' || l == 'e' || l == 'i'
                    || l == 'o' || l == 'u'
                    || l == 'á' || l == 'é' || l == 'í'
                    || l == 'ó' || l == 'ú';
            if (esVocal) {
                vocales++;
            }
            i++;
        }
        IO.println("Vocales: " + vocales);
    }
}

Salida con "Programacion":

1
2
3
4
5
Total de caracteres: 12
Primera letra: P
Última letra: n
Letra del medio: a
Vocales: 5

Puntos clave:

  1. texto.toCharArray() convierte el String en un char[].
  2. Consultas sueltas sin recorrer: letras[0], letras[length - 1], letras[length / 2].
  3. Recorrido con while para contar vocales: el patrón "pedir/recorrer elemento a elemento" con un índice i que empieza en 0 y sube hasta length - 1.

5.10.4 Ejemplo 4: swap de dos elementos y comprobación

public class SwapElementos {

    public static void main(String[] args) {

        int[] numeros = {10, 20, 30, 40, 50};

        IO.println("Antes del swap:");
        IO.println("numeros[0] = " + numeros[0] + ", numeros[4] = " + numeros[4]);

        // Swap de los extremos
        int temp = numeros[0];
        numeros[0] = numeros[numeros.length - 1];
        numeros[numeros.length - 1] = temp;

        IO.println("Después del swap:");
        IO.println("numeros[0] = " + numeros[0] + ", numeros[4] = " + numeros[4]);
    }
}

Salida:

1
2
3
4
Antes del swap:
numeros[0] = 10, numeros[4] = 50
Después del swap:
numeros[0] = 50, numeros[4] = 10

Puntos clave:

  1. Swap sin bucle: solo acceso por índice y una variable temp auxiliar.
  2. numeros.length - 1 para acceder al último elemento sin saber el tamaño de memoria.
  3. El swap es la base de muchos algoritmos de ordenación (UD6).

5.10.5 Ejemplo 5: menú con array de opciones

public class MenuOpciones {

    public static void main(String[] args) {

        String[] opciones = {
            "1. Sumar",
            "2. Restar",
            "3. Multiplicar",
            "4. Dividir",
            "5. Salir"
        };

        // Mostrar el menú con while (sin for todavía)
        IO.println("=== MENÚ ===");
        int i = 0;
        while (i < opciones.length) {
            IO.println(opciones[i]);
            i++;
        }

        // Pedir opción
        IO.print("Elige una opción: ");
        String opcion = IO.readln().trim();

        // Buscar la opción en el array con while
        boolean encontrada = false;
        int j = 0;
        while (j < opciones.length) {
            if (opciones[j].startsWith(opcion + ".")) {
                encontrada = true;
                // Aquí podríamos salir con break (sección 8)
            }
            j++;
        }

        if (encontrada) {
            IO.println("Opción válida.");
        } else {
            IO.println("Opción no válida.");
        }
    }
}

Salida de ejemplo:

1
2
3
4
5
6
7
8
=== MENÚ ===
1. Sumar
2. Restar
3. Multiplicar
4. Dividir
5. Salir
Elige una opción: 3
Opción válida.

Puntos clave:

  1. Array de Strings con valores literales para definir el menú.
  2. Mostrar el menú con while: recorre el array imprimiendo cada elemento.
  3. Buscar la opción con while: recorre el array comprobando si alguna empieza por "3.". En la sección 8 verás break para salir en cuanto se encuentre.
  4. opciones[j].startsWith(opcion + ".") comprueba si el elemento empieza por el número introducido + punto. El método startsWith se ve en UD 3.

5.11 Buenas prácticas con arrays

5.11.1 Las 10 reglas del buen uso de arrays

  1. Indica el tipo con corchetes después del tipo: int[] nums, no int nums[].
  2. Inicializa con { } si conoces los valores; con new Tipo[n] si no.
  3. .length sin paréntesis en arrays; .length() con paréntesis en String.
  4. Índices válidos: 0 a array.length - 1. El último índice siempre es length - 1.
  5. Comprueba null antes de usar si el array puede ser null.
  6. En String[] inicializado con new, los elementos son null, no "".
  7. Para swap, usa siempre temp: sin temp, pierdes un valor.
  8. Con while, nunca olvides i++ al final del cuerpo. Bucle infinito si lo olvidas.
  9. Imprime elementos sueltos o espera a Arrays.toString en la sección 6; nunca IO.println(array) directo.
  10. Pide datos al usuario con validación en el while: solo avanzas i si la entrada es válida.

5.11.2 Cuándo refactorizar (a partir de la sección 6)

Síntoma Refactoriza a
while con int i = 0; ... i++; para recorrer un array. for clásico (sección 6).
Imprimir elementos sueltos manualmente. Arrays.toString(arr) (sección 6).
Copiar elementos a un array nuevo con while. Arrays.copyOf (sección 6).
Buscar elemento a elemento con while. Arrays.binarySearch (sección 6, si está ordenado).

5.11.3 Comentar el acceso a arrays

Los comentarios buenos explican qué estás haciendo con el array, no el acceso en sí:

1
2
3
4
5
6
// MAL: explica lo obvio
int primero = arr[0];   // accede al primer elemento

// BIEN: explica la intención
int primero = arr[0];   // primer alumno de la lista
int ultimo = arr[arr.length - 1];   // último alumno (índice length-1)

5.12 Resumen y mapa conceptual

5.12.1 Las 12 ideas que no debes olvidar

  1. Un array es una secuencia fija de elementos del mismo tipo, accesibles por índice (desde 0).
  2. Tamaño fijo: no se puede cambiar una vez creado.
  3. Índices válidos: 0 a array.length - 1.
  4. Tipo[] nombre = { v1, v2, v3 }; inicializa con valores; new Tipo[n] crea con tamaño fijo y valores por defecto.
  5. .length sin paréntesis en arrays; .length() con paréntesis en String. Memorízalo.
  6. Valores por defecto: 0 para numéricos, false para boolean, null para String y objetos.
  7. El último índice siempre es length - 1: usa arr[arr.length - 1] como truco para el último.
  8. Swap con temp: para intercambiar dos valores sin perder ninguno.
  9. String.toCharArray() convierte un String en char[] para poder acceder por índice.
  10. charAt(i) es una alternativa sin crear array nuevo (más eficiente en memoria).
  11. Con while, el i++ va al final del cuerpo: si lo olvidas, bucle infinito. El for de la sección 6 te salvará de eso.
  12. No imprimas un array con IO.println(arr): verás [I@1b6d3586. Imprime elementos sueltos o espera a Arrays.toString en la sección 6.

5.12.2 Glosario rápido

Término Significado
Array Estructura de datos de tamaño fijo que contiene elementos del mismo tipo, accesibles por índice.
Array unidimensional Array de una sola dimensión, una secuencia lineal de elementos.
Tipo compuesto Tipo que agrupa varios valores bajo un mismo nombre (como el array).
Elemento Cada uno de los valores almacenados en un array.
Índice Posición de un elemento en el array. Empieza en 0.
length Campo de los arrays (sin paréntesis) que indica cuántos elementos tienen.
length() Método de String (con paréntesis) que indica cuántos caracteres tiene.
Valor por defecto El valor inicial que Java asigna a los elementos de un array creado con new: 0, false, null, etc.
ArrayIndexOutOfBoundsException Excepción que lanza Java al acceder a un índice fuera de rango.
NullPointerException Excepción que lanza Java al usar una referencia null (por ejemplo, llamar a .length() sobre un String nulo o acceder a un array null).
toCharArray() Método de String que devuelve un char[] con todos los caracteres.
charAt(i) Método de String que devuelve el carácter en la posición i (sin crear un array nuevo).
Swap Intercambio de dos valores usando una variable temp auxiliar.
Array vacío Array con 0 elementos ({} o new int[0]). Es un array legítimo, length = 0.
Array null Variable que apunta a "nada"; no es un array, y acceder a él lanza NullPointerException.

5.13 Actividades propuestas

Cinco actividades graduadas. Hazlas en orden. Las soluciones se publicarán en Aules.

5.13.1 Actividad 1 — Crear y consultar un array de Strings

Objetivo: practicar la creación con valores literales y el acceso por índice (sin recorrer).

Enunciado: Crea un array de Strings con los 12 meses del año:

String[] meses = {"Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio",
                  "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"};

Pide al usuario un número de mes (1-12), y muestra:

  1. El nombre del mes correspondiente (con meses[numero - 1], ya que el usuario escribe 1-12 pero los índices son 0-11).
  2. El mes anterior y el mes siguiente si existen (cuidado con enero y diciembre).
  3. El número de letras del nombre del mes (con meses[numero - 1].length()).

Requisitos:

  • No uses bucles. Solo acceso por índice.
  • Valida que el número esté entre 1 y 12.
  • Si el mes es enero, "mes anterior: no hay (es el primero)". Si es diciembre, "mes siguiente: no hay (es el último)".

Entrega: archivo ConsultarMes.java en el paquete jams.programacion.ud2.

Criterios evaluados: CE 3.5, CE 3.7.

5.13.2 Actividad 2 — Pedir 5 edades con while y mostrar resultados

Objetivo: practicar el patrón "pedir N datos con while y rellenar un array".

Enunciado: Escribe un programa que:

  1. Pida al usuario 5 edades.
  2. Las guarde en un array int[] edades = new int[5];.
  3. Calcule y muestre:
  4. La suma de las edades (con while).
  5. La media (con 2 decimales, usando String.format).
  6. La primera edad y la última.
  7. Cuántos alumnos son mayores de edad (>= 18), con while.

Requisitos:

  • Crea el array con new int[5].
  • Usa while para pedir.
  • Usa while para sumar y contar.

Entrega: archivo PedirEdades.java en el paquete jams.programacion.ud2.

Criterios evaluados: CE 3.5, CE 3.7.

5.13.3 Actividad 3 — Analizar caracteres con toCharArray

Objetivo: practicar la conversión String → char[] y el acceso por índice.

Enunciado: Pide al usuario una palabra con IO.readln().trim(). Conviértela en char[] con toCharArray() y muestra:

  1. Total de caracteres (letras.length).
  2. Primera letra (letras[0]).
  3. Última letra (letras[letras.length - 1]).
  4. Letra del medio (letras[letras.length / 2]).
  5. Número de vocales (con un while que recorra el array).

Requisitos:

  • Comprueba que la palabra no esté vacía antes de acceder a letras[0] (si lo está, muestra error y sale).
  • Para contar vocales, usa while con un índice i (no for, que se ve en la sección 6).

Entrega: archivo AnalizarPalabra.java en el paquete jams.programacion.ud2.

Criterios evaluados: CE 3.5, CE 3.7.

5.13.4 Actividad 4 — Swap de dos posiciones

Objetivo: practicar el swap y el acceso por índice.

Enunciado: Dado un array:

int[] numeros = {10, 20, 30, 40, 50, 60, 70, 80, 90, 100};

Pide al usuario dos posiciones i y j (entre 0 y numeros.length - 1) e intercambia los elementos en esas posiciones usando el patrón swap con temp. Muestra el array antes y después imprimiendo elementos sueltos (no Arrays.toString, que se ve en la sección 6).

Requisitos:

  • Valida que ambas posiciones estén en el rango [0, length-1].
  • Usa el patrón swap: temp = arr[i]; arr[i] = arr[j]; arr[j] = temp;.
  • Para imprimir el array "antes" y "después", concatena los elementos con + en una sola línea. Por ejemplo: "10, 20, 30, 40, 50, 60, 70, 80, 90, 100".

Entrega: archivo SwapPosiciones.java en el paquete jams.programacion.ud2.

Criterios evaluados: CE 3.5, CE 3.7.

5.13.5 Actividad 5 — Cuestionario de repaso

Responde por escrito:

  1. ¿Qué es un array y qué tres propiedades lo definen?
  2. Escribe la sintaxis para declarar un array con valores literales y con tamaño fijo.
  3. ¿Por qué numeros.length no lleva paréntesis pero texto.length() sí?
  4. ¿Cuál es el rango de índices válidos de un array de 5 elementos?
  5. ¿Qué valor por defecto tienen los elementos de new int[3], new boolean[3] y new String[3]?
  6. ¿Cuál es la diferencia entre un array vacío ({}) y un array null?
  7. ¿Cómo se escribe el "truco del último elemento"? ¿Por qué es necesario?
  8. Escribe el patrón swap para intercambiar dos elementos de un array.
  9. ¿Qué hace String.toCharArray() y para qué sirve?
  10. ¿Qué hace String.charAt(i) y en qué se diferencia de toCharArray()?
  11. ¿Por qué hay que poner i++ al final del cuerpo de un while que recorre un array? ¿Qué pasa si lo olvidas?
  12. ¿Por qué no debes imprimir un array con IO.println(arr)? ¿Qué verías?

Entrega: archivo cuestionario_seccion5.md con tus respuestas.

Criterios evaluados: CE 3.5, CE 3.7.


5.14 Lo que NO hemos visto (y dónde verlo)

Esta sección te ha enseñado los arrays unidimensionales como tipo compuesto: cómo se crean, cómo se inicializan y cómo se accede a sus elementos sueltos. Hay varias cosas relacionadas que se reservan para secciones/unidades posteriores:

Concepto ¿Por qué no aquí? ¿Dónde se ve?
Recorrido con for clásico El for se introduce en la sección 6. Sección 6 de UD2.
for-each (recorrido sin índice) Bucle distinto al for clásico. Sección 7 de UD2.
Utilidades de Arrays (copyOf, sort, binarySearch, toString) Se introducen junto con el for clásico (muchas necesitan o producen arrays que luego recorrerás). Sección 6 de UD2.
Variantes avanzadas del for (decreciente, saltos, múltiples variables, comparativa con while) Profundización del for. Sección 6 de UD2.
Matrices bidimensionales int[][] Arrays de arrays. Sección 8 de UD2.
Bucles anidados (for dentro de for) Necesitan matrices o combinaciones. Sección 8 de UD2.
break y continue Sentencias de salto de bucle. Sección 8 de UD2.
try-catch para manejar ArrayIndexOutOfBoundsException y NullPointerException Manejo de excepciones. Sección 9 de UD2.
Arrays dentados (jagged arrays) Cada fila tiene distinta longitud. UD6.
Más utilidades de Arrays (fill, equals, compare, mismatch, copyOfRange, asList) Avanzadas. UD6.
Colecciones (List, Set, Map, Queue) Estructuras dinámicas; necesitan POO y genéricos. UD6.
Iterable<T> y Iterator<T> Interfaces, genéricos y POO. UD6.
Genéricos (<T>, List<String>) Concepto avanzado de tipos parametrizados. UD6.
StringBuilder para concatenación eficiente En bucles de mucha concatenación. UD 3 (POO con la API de Java).

Cierre de la sección. Llegar hasta aquí es mérito. Acabas de aprender la primera estructura de datos del curso: el array. Es la más simple, pero también la más usada. Casi cualquier programa que maneje más de un dato del mismo tipo acaba teniendo un array por algún sitio. Y ahora ya sabes crearlos, inicializarlos, acceder a sus elementos y hacer operaciones básicas como el swap. Hemos usado while para los pocos recorridos necesarios, pero habrás notado que es algo verboso (declarar int i = 0, escribir i < arr.length, acordarse del i++...). Eso es a propósito: en la sección 6 vas a conocer el for clásico y vas a ver cómo toda esa maquinaria se simplifica en una sola línea. Es como pasar de un coche manual a uno automático: hace lo mismo, pero con menos esfuerzo y menos posibilidades de equivocarte. La regla más importante que te llevas de esta sección es **array →length, String →length()**: la confundes miles de veces a lo largo del curso. Nos vemos en la sección 6, donde elfor` clásico te va a cambiar la vida como programador.