Java Arrays Multidimensionais

Um array comum é uma fileira de valores. Mas muita coisa no mundo é uma tabela: as notas de cada aluno em cada prova, os assentos de um cinema, o tabuleiro do jogo da velha, uma planilha. Para isso o Java tem os arrays de duas dimensões, chamados de matrizes: arrays cujos elementos são outros arrays.

Você vai aprender
  • O que é uma matriz e como o Java a monta ("um array de arrays").
  • Criar com chaves e com new int[3][4].
  • Acessar e mudar um elemento com [linha][coluna].
  • Descobrir o número de linhas e de colunas com length.
  • Percorrer com dois laços, somar por linha e por coluna.
  • Mostrar a matriz inteira com Arrays.deepToString.
  • Arrays irregulares, em que cada linha tem um tamanho.

Os exemplos rodam no terminal da Ana (máquina fictícia notebook, pasta ~/java, JDK 25) com java Main.java. Antes desta aula, veja Arrays e Laços Aninhados: uma matriz é exatamente um array percorrido por dois laços.


A ideia antes do código

A professora Ana anota as notas num caderno quadriculado: uma linha para cada aluno e uma coluna para cada prova.

Prova 0Prova 1Prova 2Prova 3
Aluno 0 (Ana)7869
Aluno 1 (Bruno)10998
Aluno 2 (Carla)5676

Para achar uma nota, você precisa de dois números: a linha e a coluna. "Aluno 1, prova 2" é a nota 9 em destaque. É como a batalha naval ("B-5") ou as coordenadas de uma planilha ("C3").

Como o Java guarda isso? Com o que você já conhece: cada linha é um array comum de int ({7, 8, 6, 9}), e a tabela é um array dessas linhas. Um array de arrays.


Criar com os valores

public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        System.out.println("Aluno 0, prova 0: " + notas[0][0]);
        System.out.println("Aluno 1, prova 2: " + notas[1][2]);
        System.out.println("Aluno 2, prova 3: " + notas[2][3]);
        System.out.println("Linhas (alunos): " + notas.length);
        System.out.println("Colunas (provas): " + notas[0].length);
    }
}
$ java Main.java
Aluno 0, prova 0: 7
Aluno 1, prova 2: 9
Aluno 2, prova 3: 6
Linhas (alunos): 3
Colunas (provas): 4
int[][]
O tipo: dois pares de colchetes = duas dimensões. Leia como "array de arrays de int".
notas =
O nome e a atribuição.
{
Abre o array de fora (o das linhas).
{7, 8, 6, 9},
A linha 0: um array comum. As linhas são separadas por vírgula.
…
As linhas 1 e 2.
};
Fecha o array de fora. Escrever uma linha por linha do código deixa a tabela "desenhada", fácil de conferir.

Acessar: [linha][coluna]

notas
A matriz inteira.
[1]
Primeiro colchete: a linha 1 (o Bruno). O resultado já é um array: {10, 9, 9, 8}.
[2]
Segundo colchete: a coluna 2 dessa linha. O resultado é o int 9.

Os dois índices começam no 0, como em todo array. A ordem é sempre linha primeiro, coluna depois.

Quantas linhas e quantas colunas?

  • notas.length é o tamanho do array de fora: o número de linhas (3).
  • notas[0].length é o tamanho da linha 0: o número de colunas (4). Numa matriz "retangular", todas as linhas têm o mesmo tamanho, então qualquer linha serve.

Uma linha sozinha é um array comum, e dá para guardá-la numa variável int[]:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        int[] doBruno = notas[1];
        System.out.println(Arrays.toString(doBruno));
        System.out.println(notas[1].length);
    }
}
$ java Main.java
[10, 9, 9, 8]
4

Criar vazia, com tamanho: new int[3][4]

A sala de cinema tem 3 fileiras de 4 cadeiras. No início, todas estão livres (0). Quando alguém compra, a cadeira vira 1:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] assentos = new int[3][4];
        assentos[0][1] = 1;
        assentos[2][3] = 1;
        System.out.println(Arrays.deepToString(assentos));
    }
}
$ java Main.java
[[0, 1, 0, 0], [0, 0, 0, 0], [0, 0, 0, 1]]
new int
Cria um array novo de int.
[3]
3 linhas.
[4]
4 colunas em cada linha. Total: 12 elementos, todos com o valor padrão (0, ou 0.0, false, null, conforme o tipo; veja Arrays).

Mostrar a matriz: Arrays.deepToString

Você já sabe que println de um array mostra o "crachá" ([I@…) e que Arrays.toString mostra o conteúdo. Com matrizes há uma pegadinha:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        System.out.println(notas);
        System.out.println(Arrays.toString(notas));
        System.out.println(Arrays.deepToString(notas));
    }
}
$ java Main.java
[[I@2bbaf4f0                                     « o crachá da matriz: [[ = duas dimensões
[[I@11c20519, [I@70beb599, [I@4e41089d]          « toString: os crachás das 3 linhas
[[7, 8, 6, 9], [10, 9, 9, 8], [5, 6, 7, 6]]      « deepToString: tudo

O Arrays.toString só desce um nível: mostra os elementos do array de fora, e esses elementos são arrays, que aparecem como crachás. O Arrays.deepToString ("deep" = profundo) desce em todos os níveis. Regra: matriz → deepToString.


Percorrer com dois laços

Para passar por todos os elementos, use a receita de Laços Aninhados: o laço de fora anda pelas linhas, o de dentro pelas colunas daquela linha:

public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        for (int aluno = 0; aluno < notas.length; aluno++) {
            for (int prova = 0; prova < notas[aluno].length; prova++) {
                System.out.print(notas[aluno][prova] + "\t");
            }
            System.out.println();
        }
    }
}
$ java Main.java
7	8	6	9
10	9	9	8
5	6	7	6	
ParteO que faz
aluno < notas.lengthLaço de fora: uma volta por linha (3).
prova < notas[aluno].lengthLaço de dentro: uma volta por coluna desta linha. Usar notas[aluno].length (e não um 4 fixo) faz o código funcionar com qualquer tamanho, até com linhas de tamanhos diferentes.
notas[aluno][prova]O elemento da vez.
"\t" e println()Tabulação entre as colunas; quebra de linha no fim de cada aluno.

Com for-each

Quando os índices não importam, dois for-each dão conta. Repare no tipo do laço de fora: cada "item" da matriz é uma linha, um int[]:

public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        int total = 0;
        for (int[] linha : notas) {
            for (int nota : linha) {
                total += nota;
            }
        }
        System.out.println("Soma de todas as notas: " + total);
    }
}
$ java Main.java
Soma de todas as notas: 90

Missão: no mapa do cinema, 1 é cadeira vendida e 0 é livre. Percorra a matriz com dois laços, conte as cadeiras vendidas e mostre Total de ingressos vendidos: 5.

public class Main {
    public static void main(String[] args) {
        int[][] assentos = {
            {0, 1, 1, 0},
            {1, 0, 0, 0},
            {0, 1, 0, 1}
        };
        int vendidos = 0;
        // percorra a matriz aqui

        System.out.println("Total de ingressos vendidos: " + vendidos);
    }
}
public class Main {
    public static void main(String[] args) {
        int[][] assentos = {
            {0, 1, 1, 0},
            {1, 0, 0, 0},
            {0, 1, 0, 1}
        };
        int vendidos = 0;
        for (int[] fileira : assentos) {
            for (int cadeira : fileira) {
                if (cadeira == 1) {
                    vendidos++;
                }
            }
        }
        System.out.println("Total de ingressos vendidos: " + vendidos);
    }
}

Somar por linha e por coluna

A média de cada aluno (por linha)

O acumulador nasce dentro do laço de fora, para recomeçar do zero a cada aluno:

public class Main {
    public static void main(String[] args) {
        String[] alunos = {"Ana", "Bruno", "Carla"};
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        for (int aluno = 0; aluno < notas.length; aluno++) {
            int soma = 0;
            for (int prova = 0; prova < notas[aluno].length; prova++) {
                soma += notas[aluno][prova];
            }
            double media = (double) soma / notas[aluno].length;
            System.out.println(alunos[aluno] + ": média " + media);
        }
    }
}
$ java Main.java
Ana: média 7.5
Bruno: média 9.0
Carla: média 6.0

Se o int soma = 0; ficasse antes dos dois laços, o Bruno herdaria a soma da Ana. O lugar da variável decide o que ela acumula.

A média de cada prova (por coluna)

Para somar uma coluna, os papéis dos laços se invertem: o de fora anda pelas colunas e o de dentro pelas linhas. O acesso continua sendo notas[aluno][prova], linha primeiro:

public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        for (int prova = 0; prova < notas[0].length; prova++) {
            int soma = 0;
            for (int aluno = 0; aluno < notas.length; aluno++) {
                soma += notas[aluno][prova];
            }
            System.out.println("Prova " + (prova + 1) + ": média da turma " + (double) soma / notas.length);
        }
    }
}
$ java Main.java
Prova 1: média da turma 7.333333333333333
Prova 2: média da turma 7.666666666666667
Prova 3: média da turma 7.333333333333333
Prova 4: média da turma 7.666666666666667

As muitas casas decimais são o jeito do println mostrar um double; para arredondar na tela, use printf("%.2f", …), que você verá em Formatar Texto.


Outro tipo: o jogo da velha

Matrizes podem ser de qualquer tipo. Um tabuleiro de jogo da velha é um char[][] 3 × 3:

public class Main {
    public static void main(String[] args) {
        char[][] velha = {
            {'X', 'O', 'X'},
            {' ', 'X', 'O'},
            {'O', ' ', 'X'}
        };
        for (char[] linha : velha) {
            System.out.println(" " + linha[0] + " | " + linha[1] + " | " + linha[2]);
        }
        if (velha[0][0] == 'X' && velha[1][1] == 'X' && velha[2][2] == 'X') {
            System.out.println("X venceu na diagonal!");
        }
    }
}
$ java Main.java
 X | O | X
   | X | O
 O |   | X
X venceu na diagonal!

A diagonal principal são as posições em que a linha é igual à coluna: [0][0], [1][1], [2][2].


Arrays irregulares

Como cada linha é um array separado, nada obriga as linhas a terem o mesmo tamanho. Um array assim se chama irregular (em inglês, jagged, "dentado"). A Ana registra os minutos de corrida de cada semana, e cada semana teve um número diferente de treinos:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] treino = new int[3][];
        treino[0] = new int[] {30, 45};
        treino[1] = new int[] {20, 25, 30, 40};
        treino[2] = new int[] {60};
        System.out.println(Arrays.deepToString(treino));
        for (int semana = 0; semana < treino.length; semana++) {
            System.out.println("Semana " + (semana + 1) + ": " + treino[semana].length + " treinos");
        }
    }
}
$ java Main.java
[[30, 45], [20, 25, 30, 40], [60]]
Semana 1: 2 treinos
Semana 2: 4 treinos
Semana 3: 1 treinos
new int[3][]
Cria só o array de fora, com 3 linhas, e deixa as colunas em branco. Cada linha começa como null (ainda não existe).
treino[0] = new int[] {30, 45};
Cria a linha 0, com 2 elementos.
treino[1] = …
Cada linha recebe o seu próprio array, do tamanho que precisar.

Também dá para escrever com chaves: int[][] treino = {{30, 45}, {20, 25, 30, 40}, {60}};. Num array irregular, use sempre treino[linha].length no laço de dentro; um número fixo de colunas não funciona.

As linhas também podem ser criadas num laço. Aqui a linha i tem i + 1 elementos, formando um triângulo:

import java.util.Arrays;

public class Main {
    public static void main(String[] args) {
        int[][] triangulo = new int[4][];
        for (int i = 0; i < triangulo.length; i++) {
            triangulo[i] = new int[i + 1];
            for (int j = 0; j <= i; j++) {
                triangulo[i][j] = j + 1;
            }
        }
        System.out.println(Arrays.deepToString(triangulo));
    }
}
$ java Main.java
[[1], [1, 2], [1, 2, 3], [1, 2, 3, 4]]
E três dimensões?

Existe: int[][][] cubo = new int[2][3][4]; e o acesso é cubo[a][b][c]. Pense num prédio (andar, fileira, vaga) ou em várias planilhas (turma, aluno, prova). Na prática, passar de duas dimensões é raro; quando os dados ficam complexos assim, objetos e coleções (Nível 2 e 3) costumam ser mais claros.


Formas de criar: qual usar

FormaExemploQuando usar
Chavesint[][] m = {{1, 2}, {3, 4}};Você já sabe os valores.
new com as duas medidasint[][] m = new int[3][4];Tabela retangular a preencher depois.
new só com as linhasint[][] m = new int[3][]; + m[i] = new int[n];Linhas de tamanhos diferentes (irregular).

Quando dá errado

ArrayIndexOutOfBoundsException na segunda dimensão
public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        System.out.println(notas[1][4]);
    }
}
$ java Main.java
Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 4 out of bounds for length 4
	at Main.main(Main.java:8)

O que quer dizer: a linha 1 existe, mas ela tem 4 colunas (0 a 3), e o código pediu a coluna 4. O length 4 da mensagem é o tamanho da linha, não da matriz. Confira se não trocou linha e coluna: notas[4][1] daria o mesmo erro com length 3.

Uma coluna "sumiu": o limite errado no laço de dentro
public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        for (int i = 0; i < notas.length; i++) {
            for (int j = 0; j < notas.length; j++) {
                System.out.print(notas[i][j] + " ");
            }
            System.out.println();
        }
    }
}
$ java Main.java
7 8 6
10 9 9
5 6 7                        « a quarta prova não apareceu, e nenhum erro foi mostrado

O que aconteceu: o laço de dentro usou notas.length (3 linhas) como limite das colunas (que são 4). Como 3 é menor que 4, nada estourou: a última coluna foi simplesmente ignorada. Se fosse ao contrário (mais linhas que colunas), daria ArrayIndexOutOfBoundsException.

Como consertar: no laço de dentro, use notas[i].length.

NullPointerException num array irregular
public class Main {
    public static void main(String[] args) {
        int[][] treino = new int[3][];
        treino[0][0] = 30;
    }
}
$ java Main.java
Exception in thread "main" java.lang.NullPointerException: Cannot store to int array because "<local1>[0]" is null
	at Main.main(Main.java:4)

O que quer dizer: "não dá para guardar no array de int porque treino[0] é null" (o <local1> é a variável treino, sem o nome; veja a explicação em Arrays). Com new int[3][], as linhas ainda não existem.

Como consertar: crie a linha antes de usar: treino[0] = new int[2];. Ou, se todas as linhas têm o mesmo tamanho, new int[3][2].

"int[] cannot be converted to int": faltou um índice
public class Main {
    public static void main(String[] args) {
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        int x = notas[1];
    }
}
$ java Main.java
Main.java:8: error: incompatible types: int[] cannot be converted to int
        int x = notas[1];
                     ^
1 error
error: compilation failed

O que quer dizer: notas[1] é uma linha inteira (int[]), não um número. Para um elemento, são dois índices: notas[1][2]. Para a linha, a variável precisa ser int[].


Referência

CódigoO que faz
int[][] m = {{1, 2, 3}, {4, 5, 6}};Matriz 2 × 3 com valores
int[][] m = new int[2][3];Matriz 2 × 3 com zeros
int[][] m = new int[2][];2 linhas ainda não criadas (irregular)
m[1][2]Elemento da linha 1, coluna 2
m[1]A linha 1 inteira (um int[])
m.lengthNúmero de linhas
m[i].lengthNúmero de colunas da linha i
Arrays.deepToString(m)[[1, 2, 3], [4, 5, 6]]
for (int[] linha : m) for (int x : linha)Passa por todos os elementos

Exercícios

1. Acessar

Em int[][] m = {{1, 2, 3}, {4, 5, 6}};, complete para pegar o número 6:

int seis = m;

2. Criar

Complete para criar uma matriz de double com 12 linhas (meses) e 31 colunas (dias):

double[][] chuva =

3. Mostrar

Complete para mostrar o conteúdo inteiro da matriz m:

System.out.println(Arrays.(m));

4. Em int[][] m = new int[4][7];, quanto vale m[0].length?

5. O que Arrays.toString(m) mostra, se m é um int[][]?

6. Para somar uma coluna da matriz, o laço de fora anda por…

Missão final: mostre a soma das notas de cada aluno, uma linha por aluno: Ana: 30, Bruno: 36 e Carla: 24. Lembre de recomeçar a soma a cada aluno.

public class Main {
    public static void main(String[] args) {
        String[] alunos = {"Ana", "Bruno", "Carla"};
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        // dois laços aqui

    }
}
public class Main {
    public static void main(String[] args) {
        String[] alunos = {"Ana", "Bruno", "Carla"};
        int[][] notas = {
            {7, 8, 6, 9},
            {10, 9, 9, 8},
            {5, 6, 7, 6}
        };
        for (int i = 0; i < notas.length; i++) {
            int soma = 0;
            for (int j = 0; j < notas[i].length; j++) {
                soma += notas[i][j];
            }
            System.out.println(alunos[i] + ": " + soma);
        }
    }
}

Resumo

  • Uma matriz (int[][]) é um array de arrays: cada linha é um array comum.
  • Crie com chaves ({{…}, {…}}) ou com new int[linhas][colunas].
  • Acesse com m[linha][coluna], os dois a partir de 0.
  • m.length = linhas; m[i].length = colunas da linha i.
  • Percorra com dois laços; no de dentro, use m[i].length.
  • Para mostrar, Arrays.deepToString(m) (o toString só mostra os crachás das linhas).
  • Em arrays irregulares (new int[3][]), cada linha precisa ser criada antes de usar, senão vem NullPointerException.