Saltar al contenido
matrices.java · devschool

Matrices en Java

Lección 12 de 26 · 12 min de lectura · Actualizado el

En esta lección
  1. Qué es una matriz
  2. Declarar y crear matrices
  3. Acceder a los elementos
  4. Filas, columnas y length
  5. Recorrer una matriz
  6. Sumar filas y columnas
  7. Matrices cuadradas: diagonales
  8. La traspuesta
  9. Arrays irregulares
  10. Ejemplo completo: tres en raya
  11. Copiar matrices y más dimensiones
  12. Errores frecuentes
  13. Resumen

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 i para la fila y j para la columna. Si en tu problema hay nombres más claros, úsalos: alumno y asignatura, fila y columna.

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.length es el tamaño del array exterior, es decir, cuántas filas hay.
  • notas[0] es la primera fila, que es un int[]. Su length es 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 valen null hasta que las creas. Si intentas usar marcas[0][0] antes de marcas[0] = new int[...], obtendrás un NullPointerException.

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.length en el bucle interior: da el número de filas, no de columnas. Usa matriz[i].length.
  • Imprimir con println o Arrays.toString: salen direcciones de memoria. Usa Arrays.deepToString.
  • No reiniciar el acumulador: si declaras suma fuera 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

TareaCódigo
Crear vacíaint[][] m = new int[filas][columnas];
Crear con valoresint[][] m = {{1, 2}, {3, 4}};
Leer o cambiarm[fila][columna]
Número de filasm.length
Columnas de la fila im[i].length
RecorrerDos for anidados (filas fuera, columnas dentro)
MostrarArrays.deepToString(m)
CompararArrays.deepEquals(a, b)
Diagonal principalm[i][i] con un solo bucle
Traspuestat[j][i] = m[i][j] en una matriz nueva
Irregularnew 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

[Java] ¿Qué imprime este código?
int[][] m = {{1, 2, 3}, {4, 5, 6}};
System.out.println(m.length + " " + m[0].length);

[Java] ¿Qué imprime este código?
int[][] c = {{2, 0, 1}, {3, 5, 0}, {1, 4, 7}};
int s = 0;
for (int i = 0; i < c.length; i++) {
    s += c[i][i];
}
System.out.println(s);

[Java] Tienes int[][] m = {{1, 2}, {3, 4}}. ¿Qué instrucción muestra [[1, 2], [3, 4]]?

[Java] ¿Qué imprime este código?
int[][] a = {{1, 2}, {3, 4}};
int[][] b = a.clone();
b[0][0] = 9;
System.out.println(a[0][0]);

¿Te ha quedado claro? Márcala y verás tu progreso en el explorador.