Matrices en Java
En esta lección
Un array normal guarda una lista de valores en una sola fila. Pero muchos datos tienen forma de tabla: las notas de cada alumno en cada asignatura, los asientos de una sala de cine, las casillas de un tablero de ajedrez o las ventas de cada tienda en cada mes. Para eso Java tiene los arrays bidimensionales, a los que llamamos matrices. En esta lección aprenderás a crearlas, a recorrerlas con bucles anidados y a resolver las operaciones típicas: sumar filas y columnas, recorrer la diagonal o calcular la traspuesta.
Qué es una matriz
Una matriz es una tabla de valores del mismo tipo organizada en filas y columnas. Cada elemento se localiza con dos índices: primero la fila y después la columna, y los dos empiezan en 0.
columna 0 columna 1 columna 2
fila 0 7 8 6
fila 1 5 4 9
fila 2 10 9 8
fila 3 4 6 5
Esta matriz tiene 4 filas y 3 columnas (se dice que es de 4 × 3). El número 9 de la fila 1 está en la posición [1][2].
En Java, una matriz es en realidad un array de arrays: un array cuyos elementos son, a su vez, arrays. Esta idea explica todo lo que verás después, así que tenla presente.
Declarar y crear matrices
Se declaran con dos pares de corchetes:
int[][] notas; // una matriz de enteros (todavía sin crear)
Con un tamaño fijo
Con new indicas cuántas filas y cuántas columnas quieres. Todas las casillas se rellenan con el valor por defecto del tipo: 0 para números, false para boolean, '\u0000' para char y null para objetos.
int[][] asientos = new int[3][4]; // 3 filas y 4 columnas, todo a 0
System.out.println(Arrays.deepToString(asientos));
// [[0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]
Con valores iniciales
Si ya conoces los datos, escríbelos entre llaves: cada fila es un grupo de llaves dentro de las llaves exteriores.
// Notas de 4 alumnos (filas) en 3 asignaturas (columnas)
int[][] notas = {
{7, 8, 6},
{5, 4, 9},
{10, 9, 8},
{4, 6, 5}
};
Escribir cada fila en su propia línea no es obligatorio, pero ayuda mucho a “ver” la tabla.
Acceder a los elementos
Primero la fila, luego la columna:
System.out.println(notas[1][2]); // 9 (fila 1, columna 2)
System.out.println(notas[2][0]); // 10 (fila 2, columna 0)
notas[3][0] = 5; // cambia el 4 de la última fila por un 5
Si alguno de los dos índices se sale de rango, obtienes el mismo error que con un array normal:
System.out.println(notas[0][3]);
// ArrayIndexOutOfBoundsException: Index 3 out of bounds for length 3
Consejo: en matemáticas y en casi todo el código se usa
ipara la fila yjpara la columna. Si en tu problema hay nombres más claros, úsalos:alumnoyasignatura,filaycolumna.
Filas, columnas y length
Como una matriz es un array de arrays, length significa cosas distintas según dónde lo pongas:
System.out.println(notas.length); // 4: número de filas
System.out.println(notas[0].length); // 3: número de columnas de la fila 0
notas.lengthes el tamaño del array exterior, es decir, cuántas filas hay.notas[0]es la primera fila, que es unint[]. Sulengthes cuántas columnas tiene esa fila.
notas[0] es un array normal y corriente, así que puedes usarlo como tal:
int[] primeraFila = notas[0];
System.out.println(Arrays.toString(primeraFila)); // [7, 8, 6]
Recorrer una matriz
Para visitar todas las casillas necesitas dos bucles anidados: el exterior recorre las filas y el interior, las columnas de cada fila. Repasa los bucles si lo necesitas.
for (int i = 0; i < notas.length; i++) { // cada fila
for (int j = 0; j < notas[i].length; j++) { // cada columna de esa fila
System.out.print(notas[i][j] + "\t");
}
System.out.println(); // salto al acabar la fila
}
7 8 6
5 4 9
10 9 8
4 6 5
Fíjate en la condición del bucle interior: j < notas[i].length, la longitud de la fila actual. Así funciona siempre, incluso cuando las filas tienen tamaños distintos (lo verás más abajo).
Si no necesitas los índices, el for-each es más corto. Cada vuelta del bucle exterior te da una fila completa, que es un int[]:
for (int[] fila : notas) {
for (int nota : fila) {
System.out.print(nota + " ");
}
System.out.println();
}
Mostrar una matriz de golpe: Arrays.deepToString
System.out.println(notas) no muestra los datos, sino algo como [[I@10d68fcd (el tipo y una dirección de memoria). Y Arrays.toString(notas) tampoco sirve, porque muestra las direcciones de cada fila. Para matrices existe Arrays.deepToString, que entra en cada fila:
import java.util.Arrays;
System.out.println(Arrays.deepToString(notas));
// [[7, 8, 6], [5, 4, 9], [10, 9, 8], [4, 6, 5]]
Es perfecto para depurar. Para mostrar una tabla bonita al usuario, usa los bucles anidados con printf.
Sumar filas y columnas
Es la operación más habitual. Con la matriz de notas, la media de cada alumno se obtiene sumando su fila:
String[] alumnos = {"Ana", "Luis", "Marta", "Pablo"};
for (int i = 0; i < notas.length; i++) {
int suma = 0; // se reinicia en cada fila
for (int j = 0; j < notas[i].length; j++) {
suma += notas[i][j];
}
double media = (double) suma / notas[i].length;
System.out.printf("%-6s media %.2f%n", alumnos[i], media);
}
Ana media 7.00
Luis media 6.00
Marta media 9.00
Pablo media 5.00
La media de cada asignatura se obtiene sumando su columna. Para eso se intercambian los bucles: el exterior recorre las columnas y el interior, las filas.
String[] asignaturas = {"Programación", "Bases de datos", "Lenguajes de marcas"};
for (int j = 0; j < notas[0].length; j++) { // cada columna
int suma = 0;
for (int i = 0; i < notas.length; i++) { // cada fila
suma += notas[i][j];
}
System.out.printf("%-20s media %.2f%n", asignaturas[j], (double) suma / notas.length);
}
Programación media 6.50
Bases de datos media 6.75
Lenguajes de marcas media 7.00
Dos detalles importantes: la variable suma se declara dentro del bucle exterior para que empiece en 0 con cada fila o columna, y la conversión (double) evita la división entera (lo viste en operadores).
Matrices cuadradas: diagonales
Una matriz es cuadrada cuando tiene el mismo número de filas que de columnas. En ellas tiene sentido hablar de diagonales:
1 2 3 diagonal principal: 1, 5, 9 (fila == columna)
4 5 6 diagonal secundaria: 3, 5, 7 (fila + columna == n - 1)
7 8 9
Para recorrer una diagonal no hace falta un bucle anidado: basta uno, porque la columna se calcula a partir de la fila.
int[][] m = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int principal = 0;
int secundaria = 0;
for (int i = 0; i < m.length; i++) {
principal += m[i][i]; // [0][0], [1][1], [2][2]
secundaria += m[i][m.length - 1 - i]; // [0][2], [1][1], [2][0]
}
System.out.println(principal + " " + secundaria); // 15 15
La traspuesta
La traspuesta de una matriz convierte las filas en columnas: lo que estaba en [i][j] pasa a [j][i]. Si la original es de 2 × 3, la traspuesta es de 3 × 2, así que hay que crear una matriz nueva con las dimensiones cambiadas.
int[][] ventas = {
{1, 2, 3},
{4, 5, 6}
};
int[][] traspuesta = new int[ventas[0].length][ventas.length]; // 3 x 2
for (int i = 0; i < ventas.length; i++) {
for (int j = 0; j < ventas[i].length; j++) {
traspuesta[j][i] = ventas[i][j];
}
}
System.out.println(Arrays.deepToString(traspuesta)); // [[1, 4], [2, 5], [3, 6]]
Es útil, por ejemplo, cuando tienes las ventas por tienda y mes y quieres verlas por mes y tienda.
Arrays irregulares
Como cada fila es un array independiente, las filas no tienen por qué medir lo mismo. A estas matrices se las llama irregulares (en inglés, jagged arrays). Se crean indicando solo el número de filas y creando después cada fila:
// Marcas de salto de 3 deportistas; cada uno hizo un número distinto de intentos
int[][] marcas = new int[3][];
marcas[0] = new int[] {12, 15};
marcas[1] = new int[] {9, 11, 14, 10};
marcas[2] = new int[] {20};
System.out.println(Arrays.deepToString(marcas));
// [[12, 15], [9, 11, 14, 10], [20]]
for (int i = 0; i < marcas.length; i++) {
System.out.println("Deportista " + (i + 1) + ": " + marcas[i].length + " intentos");
}
Deportista 1: 2 intentos
Deportista 2: 4 intentos
Deportista 3: 1 intentos
También se pueden escribir directamente con valores iniciales, como este triángulo:
int[][] triangulo = {
{1},
{1, 1},
{1, 2, 1},
{1, 3, 3, 1}
};
System.out.println(triangulo[3].length); // 4
Aquí se ve por qué hay que usar matriz[i].length en el bucle interior: matriz[0].length solo sirve cuando todas las filas miden lo mismo.
Cuidado: con
new int[3][], las filas valennullhasta que las creas. Si intentas usarmarcas[0][0]antes demarcas[0] = new int[...], obtendrás unNullPointerException.
Ejemplo completo: tres en raya
Un tablero de tres en raya es una matriz de 3 × 3 de char. Vamos a mostrarlo y a comprobar si un jugador ha ganado revisando filas, columnas y las dos diagonales:
import java.util.Arrays;
public class TresEnRaya {
static void mostrar(char[][] tablero) {
for (char[] fila : tablero) {
for (char casilla : fila) {
System.out.print(casilla + " ");
}
System.out.println();
}
}
static boolean haGanado(char[][] t, char jugador) {
for (int i = 0; i < 3; i++) {
// fila i completa
if (t[i][0] == jugador && t[i][1] == jugador && t[i][2] == jugador) {
return true;
}
// columna i completa
if (t[0][i] == jugador && t[1][i] == jugador && t[2][i] == jugador) {
return true;
}
}
// diagonal principal
if (t[0][0] == jugador && t[1][1] == jugador && t[2][2] == jugador) {
return true;
}
// diagonal secundaria
return t[0][2] == jugador && t[1][1] == jugador && t[2][0] == jugador;
}
public static void main(String[] args) {
char[][] tablero = new char[3][3];
for (char[] fila : tablero) {
Arrays.fill(fila, '-'); // todas las casillas vacías
}
tablero[0][0] = 'X';
tablero[0][1] = 'O';
tablero[1][1] = 'X';
tablero[1][0] = 'O';
tablero[2][2] = 'X';
mostrar(tablero);
System.out.println("¿Gana X? " + haGanado(tablero, 'X'));
System.out.println("¿Gana O? " + haGanado(tablero, 'O'));
}
}
X O -
O X -
- - X
¿Gana X? true
¿Gana O? false
Arrays.fill rellena un array con un valor; como cada fila es un array, lo aplicamos fila a fila. Si quisieras leer las jugadas del usuario, combinarías esto con Scanner.
Copiar matrices y más dimensiones
Copiar una matriz tiene trampa. clone() o Arrays.copyOf sobre la matriz copian el array exterior, pero las filas siguen siendo las mismas: los dos “arrays de arrays” apuntan a las mismas filas.
int[][] copia = notas.clone();
copia[0][0] = 0;
System.out.println(notas[0][0]); // 0: ¡también ha cambiado el original!
Para una copia independiente, copia cada fila:
int[][] copiaReal = new int[notas.length][];
for (int i = 0; i < notas.length; i++) {
copiaReal[i] = Arrays.copyOf(notas[i], notas[i].length);
}
Lo mismo pasa al comparar: Arrays.equals compara las referencias de las filas y devuelve false aunque los números coincidan. Usa Arrays.deepEquals(a, b).
Java admite también más de dos dimensiones: int[][][] cubo = new int[2][3][4]; sería, por ejemplo, 2 edificios con 3 plantas y 4 puertas por planta. Se recorren con tres bucles anidados, pero en la práctica rara vez pasan de dos.
Errores frecuentes
- Confundir filas y columnas:
matriz[fila][columna], siempre en ese orden. - Usar
matriz.lengthen el bucle interior: da el número de filas, no de columnas. Usamatriz[i].length. - Imprimir con
printlnoArrays.toString: salen direcciones de memoria. UsaArrays.deepToString. - No reiniciar el acumulador: si declaras
sumafuera del bucle exterior, cada fila suma también lo de las anteriores. - Usar filas sin crear en un array irregular:
NullPointerException. - Creer que
clone()copia toda la matriz: solo copia el primer nivel.
Resumen
| Tarea | Código |
|---|---|
| Crear vacía | int[][] m = new int[filas][columnas]; |
| Crear con valores | int[][] m = {{1, 2}, {3, 4}}; |
| Leer o cambiar | m[fila][columna] |
| Número de filas | m.length |
Columnas de la fila i | m[i].length |
| Recorrer | Dos for anidados (filas fuera, columnas dentro) |
| Mostrar | Arrays.deepToString(m) |
| Comparar | Arrays.deepEquals(a, b) |
| Diagonal principal | m[i][i] con un solo bucle |
| Traspuesta | t[j][i] = m[i][j] en una matriz nueva |
| Irregular | new int[filas][] y luego cada fila con su tamaño |
Las matrices tienen tamaño fijo. Cuando necesites tablas que crecen, usarás colecciones como las listas. El siguiente paso del curso es la base de la programación orientada a objetos: clases y objetos.
Pon a prueba lo que has aprendido
¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.