Arrays en Java
En esta lección
Un array guarda varios valores del mismo tipo bajo un único nombre, uno detrás de otro, y con un tamaño fijo. Es la estructura de datos más básica de Java y la base de muchas otras. En esta lección aprenderás a crear arrays, a leerlos y recorrerlos, a calcular máximos, mínimos y medias, a usar la clase Arrays para ordenar, copiar y comparar, y a evitar los errores típicos, sobre todo el de copiar un array “sin copiarlo”.
Por qué existen los arrays
Imagina que quieres guardar las temperaturas máximas de una semana. Con lo que sabes de variables, necesitarías siete:
int lunes = 18, martes = 21, miercoles = 25, jueves = 23;
int viernes = 19, sabado = 16, domingo = 14;
Ahora calcula la media, o busca el día más caluroso. Tienes que escribir cada variable a mano, y si fueran las 365 temperaturas del año sería imposible. Con un array guardas todas en una sola variable y las recorres con un bucle:
int[] temperaturas = {18, 21, 25, 23, 19, 16, 14};
Cada valor ocupa una posición (o índice) numerada desde 0:
índice: 0 1 2 3 4 5 6
temperaturas: 18 21 25 23 19 16 14
Declarar y crear un array
Un array se declara poniendo [] detrás del tipo:
int[] notas; // un array de enteros, todavía sin crear
String[] nombres; // un array de textos
Java también admite int notas[], heredado del lenguaje C, pero la forma habitual es int[] notas: así el tipo completo (int[], “array de int”) queda junto.
Declarar no crea el array. Hay dos formas de crearlo.
Con new y un tamaño
int[] notas = new int[5]; // 5 posiciones
double[] precios = new double[3]; // 3 posiciones
El tamaño puede ser cualquier expresión entera, también una variable que se conoce al ejecutar el programa:
int alumnos = 4; // podría venir de un Scanner
int[] notas = new int[alumnos];
Pero una vez creado, el tamaño ya no cambia. Si necesitas una lista que crezca, usa un ArrayList, que verás en ArrayList y listas.
Con valores iniciales
Si ya conoces los valores, escríbelos entre llaves. Java cuenta cuántos hay y crea el array de ese tamaño:
int[] temperaturas = {18, 21, 25, 23, 19, 16, 14};
String[] dias = {"lunes", "martes", "miércoles", "jueves", "viernes", "sábado", "domingo"};
Las llaves a secas solo se pueden usar en la misma línea de la declaración. En cualquier otro sitio, por ejemplo al pasar un array a un método o al reasignar una variable, hay que escribir new int[] delante:
temperaturas = new int[] {20, 22, 24}; // correcto
// temperaturas = {20, 22, 24}; // no compila
Valores por defecto
Cuando creas un array con new, Java rellena todas las posiciones con un valor por defecto que depende del tipo:
| Tipo | Valor por defecto |
|---|---|
int, long, short, byte | 0 |
double, float | 0.0 |
boolean | false |
char | el carácter de código 0 ('\u0000'), que no se ve |
String y cualquier objeto | null |
System.out.println(Arrays.toString(new int[5])); // [0, 0, 0, 0, 0]
System.out.println(Arrays.toString(new boolean[2])); // [false, false]
System.out.println(Arrays.toString(new String[3])); // [null, null, null]
Acceder a los elementos y length
Cada elemento se lee o se cambia con el nombre del array y el índice entre corchetes:
int[] temperaturas = {18, 21, 25, 23, 19, 16, 14};
System.out.println(temperaturas[0]); // 18 (el primero)
System.out.println(temperaturas[6]); // 14 (el último)
temperaturas[2] = 26; // el miércoles fueron 26 grados
La propiedad length indica cuántos elementos tiene. Se escribe sin paréntesis, a diferencia del método length() de los String:
System.out.println(temperaturas.length); // 7
System.out.println(temperaturas[temperaturas.length - 1]); // 14
Como los índices empiezan en 0, el último elemento siempre está en length - 1. Usar length en vez de escribir el número a mano hace que el código siga funcionando si el array cambia de tamaño.
ArrayIndexOutOfBoundsException
Si usas un índice que no existe, el programa compila, pero al ejecutarse se detiene con una excepción:
System.out.println(temperaturas[7]);
// ArrayIndexOutOfBoundsException: Index 7 out of bounds for length 7
System.out.println(temperaturas[-1]);
// ArrayIndexOutOfBoundsException: Index -1 out of bounds for length 7
El mensaje dice qué índice pediste y cuál es la longitud. Los índices válidos van de 0 a length - 1, así que un array de 7 elementos no tiene posición 7. Es el error más típico con arrays, casi siempre por escribir <= en lugar de < en un bucle. Aprenderás a capturar excepciones en excepciones, pero lo correcto aquí es no provocarla.
Recorrer un array
Con for clásico
Cuando necesitas la posición, por ejemplo para relacionar dos arrays o para modificar elementos:
for (int i = 0; i < temperaturas.length; i++) {
System.out.println(dias[i] + ": " + temperaturas[i] + " ºC");
}
lunes: 18 ºC
martes: 21 ºC
miércoles: 26 ºC
jueves: 23 ºC
viernes: 19 ºC
sábado: 16 ºC
domingo: 14 ºC
Con for-each
Cuando solo necesitas leer cada valor, el for-each es más corto y no puede salirse de rango:
int suma = 0;
for (int t : temperaturas) {
suma += t;
}
Se lee “para cada t de temperaturas”. Pero ojo: la variable del for-each es una copia del valor. Cambiarla no cambia el array:
int[] puntos = {5, 3, 8};
for (int p : puntos) {
p = p * 2; // solo cambia la copia
}
System.out.println(Arrays.toString(puntos)); // [5, 3, 8]
for (int i = 0; i < puntos.length; i++) {
puntos[i] = puntos[i] * 2; // cambia el array de verdad
}
System.out.println(Arrays.toString(puntos)); // [10, 6, 16]
Operaciones típicas
Estos patrones aparecen en casi todos los ejercicios. Merece la pena saberlos escribir sin mirar.
Suma y media
int suma = 0;
for (int t : temperaturas) {
suma += t;
}
double media = (double) suma / temperaturas.length;
System.out.printf("Media: %.2f%n", media); // Media: 19.57
El (double) evita la división entera, que daría 19.
Máximo y mínimo
La idea: supón que el primero es el mayor y compáralo con el resto. No empieces en 0 como valor inicial: si todas las temperaturas fueran negativas, el máximo saldría 0.
int max = temperaturas[0];
int min = temperaturas[0];
int posMax = 0;
for (int i = 1; i < temperaturas.length; i++) {
if (temperaturas[i] > max) {
max = temperaturas[i];
posMax = i; // recuerda dónde estaba
}
if (temperaturas[i] < min) {
min = temperaturas[i];
}
}
System.out.println(max + " " + min); // 26 14
System.out.println("Día más caluroso: " + dias[posMax]); // Día más caluroso: miércoles
Contar y buscar
int calurosos = 0;
for (int t : temperaturas) {
if (t >= 20) {
calurosos++;
}
}
System.out.println(calurosos); // 3
int posicion = -1; // -1 significa "no encontrado"
for (int i = 0; i < dias.length; i++) {
if (dias[i].equals("jueves")) {
posicion = i;
break; // ya lo tenemos, no seguimos
}
}
System.out.println(posicion); // 3
Para comparar String usa siempre equals, nunca == (lo viste en String y StringBuilder).
La clase Arrays
El paquete java.util incluye la clase Arrays, con métodos ya hechos para las tareas más comunes. Necesitas importarla al principio del archivo:
import java.util.Arrays;
toString: mostrar el contenido
Si imprimes un array directamente, no ves sus valores, sino el tipo y un código ([I significa “array de int”):
int[] edades = {34, 19, 52, 27, 19};
System.out.println(edades); // [I@5f683daf
System.out.println(Arrays.toString(edades)); // [34, 19, 52, 27, 19]
sort: ordenar
Ordena el array en el sitio, es decir, modifica el original de menor a mayor:
Arrays.sort(edades);
System.out.println(Arrays.toString(edades)); // [19, 19, 27, 34, 52]
String[] socios = {"Pablo", "ana", "Lucía", "Beatriz"};
Arrays.sort(socios);
System.out.println(Arrays.toString(socios)); // [Beatriz, Lucía, Pablo, ana]
Los textos se ordenan por el código de cada carácter, y las mayúsculas van antes que las minúsculas: por eso ana queda la última. Para otros órdenes se usa un Comparator, que verás en ArrayList y listas.
fill: rellenar
int[] marcador = new int[4];
Arrays.fill(marcador, -1);
System.out.println(Arrays.toString(marcador)); // [-1, -1, -1, -1]
copyOf y copyOfRange: copiar
copyOf crea un array nuevo con la longitud que indiques. Si es mayor, rellena con valores por defecto; si es menor, recorta:
int[] original = {1, 2, 3};
System.out.println(Arrays.toString(Arrays.copyOf(original, 5))); // [1, 2, 3, 0, 0]
System.out.println(Arrays.toString(Arrays.copyOf(original, 2))); // [1, 2]
int[] trozo = Arrays.copyOfRange(new int[] {10, 20, 30, 40, 50}, 1, 4);
System.out.println(Arrays.toString(trozo)); // [20, 30, 40] (del 1 al 4, sin incluir el 4)
Es la forma de “hacer crecer” un array: crear uno más grande y reasignar la variable. Si lo haces a menudo, necesitas un ArrayList.
equals: comparar contenido
== compara si dos variables apuntan al mismo array, no si tienen los mismos valores:
int[] a = {1, 2, 3};
int[] b = {1, 2, 3};
System.out.println(a == b); // false: son dos arrays distintos
System.out.println(Arrays.equals(a, b)); // true: mismo contenido
binarySearch: buscar en un array ordenado
La búsqueda binaria es muy rápida: mira el centro, descarta la mitad donde no puede estar el valor y repite. Por eso solo funciona si el array está ordenado. Devuelve la posición, o un número negativo si no lo encuentra:
int[] ordenado = {3, 8, 15, 22, 40};
System.out.println(Arrays.binarySearch(ordenado, 22)); // 3
System.out.println(Arrays.binarySearch(ordenado, 10)); // -3 (no está)
System.out.println(Arrays.binarySearch(new int[] {40, 3, 22, 8}, 40)); // -5: ¡mal!
En el último caso el 40 está en la posición 0, pero como el array no está ordenado, el resultado no tiene sentido. Ordena antes con Arrays.sort o usa un bucle normal.
Copiar arrays: referencia frente a copia
Esta es la trampa más importante de la lección. Una variable de tipo array no guarda los valores, sino la dirección donde está el array en memoria (lo viste en variables). Por eso, al asignar un array a otra variable no se copia nada: las dos variables apuntan al mismo array.
int[] precios = {10, 20, 30};
int[] otro = precios; // NO es una copia: es otro nombre para el mismo array
otro[0] = 99;
System.out.println(Arrays.toString(precios)); // [99, 20, 30]
precios ──┐
├──> [99, 20, 30]
otro ─────┘
Para tener una copia independiente, crea un array nuevo. Hay tres formas equivalentes:
int[] precios = {10, 20, 30};
int[] copia1 = precios.clone();
int[] copia2 = Arrays.copyOf(precios, precios.length);
int[] copia3 = new int[precios.length];
System.arraycopy(precios, 0, copia3, 0, precios.length); // origen, posición de origen, destino, posición de destino, cuántos
copia1[0] = 99;
System.out.println(Arrays.toString(precios)); // [10, 20, 30]: el original no cambia
System.out.println(Arrays.toString(copia1)); // [99, 20, 30]
System.arraycopy permite además copiar un trozo en cualquier posición del destino.
Al pasar un array a un método ocurre lo mismo que al asignarlo: el método recibe la dirección, así que puede modificar el contenido del original. Lo tienes explicado con detalle en métodos.
Arrays de objetos
Un array puede guardar cualquier tipo, también String o objetos de tus propias clases. En ese caso, cada posición guarda una referencia a un objeto, y hasta que no la rellenas vale null:
String[] equipo = new String[3];
equipo[0] = "Irene";
System.out.println(Arrays.toString(equipo)); // [Irene, null, null]
System.out.println(equipo[1].length()); // NullPointerException
Llamar a un método sobre null lanza NullPointerException: no hay ningún objeto al que preguntarle su longitud. Antes de usar una posición, asegúrate de que la has rellenado.
Con tus propias clases funciona igual. Este carrito guarda objetos Producto (las clases se explican en la lección de clases y objetos):
public class Tienda {
public static void main(String[] args) {
Producto[] carrito = new Producto[3];
carrito[0] = new Producto("Teclado", 24.99);
carrito[1] = new Producto("Ratón", 12.50);
carrito[2] = new Producto("Alfombrilla", 6.00);
double total = 0;
for (Producto p : carrito) {
System.out.println(p.nombre + ": " + p.precio + " €");
total += p.precio;
}
System.out.printf("Total: %.2f €%n", total);
}
}
class Producto {
String nombre;
double precio;
Producto(String nombre, double precio) {
this.nombre = nombre;
this.precio = precio;
}
}
Teclado: 24.99 €
Ratón: 12.5 €
Alfombrilla: 6.0 €
Total: 43.49 €
Más allá de los arrays
Para datos en forma de tabla (notas por alumno y asignatura, un tablero de juego) se usan arrays de arrays: las matrices. Y si no sabes cuántos elementos tendrás, usa un ArrayList, que crece solo: lo tienes en ArrayList y listas.
Errores frecuentes
- Salirse del array:
i <= array.lengthen el bucle. La condición correcta esi < array.length. - Escribir
length()con paréntesis. En arrays eslength; enString,length(); en listas,size(). - Imprimir el array con
println: sale[I@.... UsaArrays.toString. - Comparar arrays con
==: usaArrays.equals. - Creer que
b = acopia el array: solo copia la dirección. Usaclone()oArrays.copyOf. - Modificar con for-each: la variable del bucle es una copia.
- Usar
binarySearchsin ordenar antes. - Iniciar el máximo en 0: empieza por el primer elemento del array.
- Olvidar
import java.util.Arrays;:cannot find symbol.
Resumen
| Tarea | Código |
|---|---|
| Crear con tamaño | int[] a = new int[5]; |
| Crear con valores | int[] a = {4, 8, 15}; |
| Leer / cambiar | a[0], a[0] = 7; |
| Longitud | a.length |
| Último elemento | a[a.length - 1] |
| Recorrer | for (int i = 0; i < a.length; i++) o for (int x : a) |
| Mostrar | Arrays.toString(a) |
| Ordenar | Arrays.sort(a) |
| Rellenar | Arrays.fill(a, valor) |
| Copiar | a.clone(), Arrays.copyOf(a, n), System.arraycopy |
| Comparar contenido | Arrays.equals(a, b) |
| Buscar (ordenado) | Arrays.binarySearch(a, valor) |
Los arrays tienen tamaño fijo, índices desde 0 y guardan referencias cuando contienen objetos. Con eso claro, el siguiente paso son las matrices, y después, las clases y objetos.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.