Двумерные массивы
Ряд шкафчиков хорош для списка: баллы игроков, цены, оценки. Но многие данные устроены как таблица. Место в кинозале — это ряд и номер кресла, клетка на игровом поле — строка и столбец.
В спортзале шкафчики тоже часто стоят стеной: несколько рядов один над другим. Чтобы найти шкафчик, нужны два номера — ряд и место в ряду. В Java такую стену собирают из обычных массивов: у каждого ряда свой массив, а ещё один массив хранит талоны ко всем рядам.
public class Main {
public static void main(String[] args) {
int[][] grid = new int[2][3];
grid[1][2] = 7;
System.out.println(grid[1][2]); // 7
System.out.println(grid[0][0]); // 0
System.out.println(grid.length); // 2
System.out.println(grid[0].length); // 3
}
}Давайте разберём программу шаг за шагом:
new int[2][3]создаёт таблицу из двух строк по три элемента, и во всех элементах лежит0.grid[1][2] = 7;кладёт7во вторую строку, в третий столбец.grid[1][2]печатает7.grid[0][0]— первый элемент первой строки, в него ничего не клали:0.grid.lengthпечатает2, аgrid[0].length—3.
Массив массивов
Двумерный массив — массив, элементы которого тоже массивы. Тип int[][] читается как «массив массивов int». Запись new int[2][3] создаёт внешний массив из двух элементов, и каждый элемент — ссылка на свою строку, массив int[3].
Индексы: строка и столбец
Запись grid[1][2] читается слева направо:
grid[1]берёт из внешнего массива вторую строку. Это обычныйint[].[2]берёт из этой строки третий элемент.
Поэтому первый индекс выбирает строку, а второй — столбец, то есть место в строке. Оба считаются с нуля, как в любом массиве.
Две длины
У таблицы два размера, и каждый берут у своего массива:
grid.length— длина внешнего массива, то есть число строк:2;grid[0].length— длина первой строки, то есть число столбцов:3;- всего элементов
2 * 3, то есть шесть, ноgrid.lengthих не считает.
На собеседовании любят спросить, что вернёт grid.length для new int[2][3]. Ответ — 2, число строк, а не число всех элементов.
Обход вложенными циклами
Таблицу можно задать и значениями сразу: каждая пара внутренних фигурных скобок — одна строка. Чтобы обойти все элементы, нужны два цикла, один внутри другого:
int[][] table = {{1, 2, 3}, {4, 5, 6}};
int sum = 0;
for (int i = 0; i < table.length; i++) {
for (int j = 0; j < table[i].length; j++) {
sum = sum + table[i][j];
}
}
System.out.println(sum); // 21Разберём, как работают циклы:
- Внешний цикл идёт по строкам:
iпробегает0и1, покаi < table.length. - Внутренний цикл идёт по столбцам текущей строки:
jпробегает0,1и2, покаj < table[i].length. - На каждой строке внутренний цикл проходит целиком, поэтому тело выполняется шесть раз.
sumсобирает все шесть элементов и печатает21.
Здесь картинка со стеной шкафчиков неточна. В настоящей стене все ряды одной длины, а в Java каждая строка — отдельный массив, и строки могут быть разной длины: {{1, 2}, {3, 4, 5}}. Поэтому во внутреннем цикле надёжнее писать table[i].length, длину именно текущей строки.
Печать таблицы
Arrays.toString печатает один массив, поэтому для таблицы его хватает только на одну строку. Всю таблицу печатает Arrays.deepToString:
System.out.println(Arrays.toString(table[1])); // [4, 5, 6]
System.out.println(Arrays.deepToString(table)); // [[1, 2, 3], [4, 5, 6]]Если отдать всю таблицу в Arrays.toString, напечатаются не числа, а служебные записи вида [I@ для каждой строки. Элементы внешнего массива — ссылки, и toString печатает их. Метод deepToString проходит по каждой ссылке и печатает саму строку.
Частая ошибка
Перепутали две длины в заголовках циклов:
int[][] table = {{1, 2, 3}, {4, 5, 6}};
int sum = 0;
for (int i = 0; i < table[0].length; i++) { // ✗
for (int j = 0; j < table.length; j++) {
sum = sum + table[i][j];
}
}Exception in thread "main" java.lang.ArrayIndexOutOfBoundsException: Index 2 out of bounds for length 2Внешний цикл выбирает строку, но его граница — table[0].length, число столбцов, то есть 3. На третьем круге i становится 2, а строк всего две, с индексами 0 и 1. Строки ограничивает table.length, а столбцы — table[i].length.
Главное
- Двумерный массив — массив массивов:
new int[2][3]создаёт две строки по три элемента. - В
grid[i][j]первый индекс выбирает строку, а второй — столбец. grid.length— число строк, аgrid[0].length— длина первой строки, то есть число столбцов.- Таблицу обходят вложенными циклами: внешний до
grid.length, внутренний доgrid[i].length.