Многомерные массивы в Java
Многомерный массив в Java — это массив, элементами которого являются другие массивы. В отличие от языков, где двумерный массив — это единая матрица в памяти, в Java он представляет собой «массив массивов»: внешний массив хранит ссылки на вложенные. Чаще всего на практике используются двумерные массивы.
Объявление двумерного массива
При объявлении переменной многомерного массива для каждого дополнительного измерения используется отдельная пара квадратных скобок. Например, так создаётся двумерный массив из 5 строк по 4 столбца:
int[][] twoD = new int[5][4]; Следующий рисунок показывает, как можно визуально представить двумерный массив 5 на 4. Левый индекс определяет строку, а правый — столбец:

Заполнение и перебор в цикле
Следующий пример демонстрирует, каким образом можно установить значения в двумерный массив 5x4. Для перебора строк используется внешний цикл for, для перебора столбцов — внутренний. Каждому следующему элементу присваивается значение на единицу большее, чем предыдущее:
public class TwoDArrayExample1 {
public static void main(String[] args) {
int[][] twoD = new int[5][4];
int value = 0;
for (int i = 0; i < 5; i++) {
for (int j = 0; j < 4; j++) {
twoD[i][j] = value++;
System.out.print(twoD[i][j] + " ");
}
System.out.println();
}
}
} Вывод программы:
0 1 2 3
4 5 6 7
8 9 10 11
12 13 14 15
16 17 18 19 Здесь массив выводится поэлементно в цикле. Чтобы напечатать содержимое многомерного массива одной строкой, есть метод Arrays.deepToString() — подробнее в отдельном уроке «Метод Arrays.deepToString()».
Как многомерный массив хранится в памяти
Рассмотрим теперь, как представлен двумерный массив int[][] twoD = new int[3][4]; в памяти. Переменная twoD указывает не на матрицу, а на массив (красного цвета), состоящий из трёх элементов. Значение каждого элемента — это ссылка на строку из четырёх элементов (фиолетового цвета):

Следующая картинка показывает, каким образом хранится трёхмерный массив int[][][] threeD = new int[3][4][2]; в памяти:

Подобным образом хранится в памяти массив любой размерности.
Важно
Каждая строка двумерного массива — самостоятельный объект в куче. Если объявить массив как new int[4][] и обратиться к строке до выделения под неё памяти, получите NullPointerException: вложенные ссылки по умолчанию равны null.
Зубчатые массивы: строки разной длины
В двумерных массивах, которые мы рассматривали до сих пор, количество элементов в каждой строке одинаково — чаще всего так и бывает. Но это не обязательно: каждая строка может содержать разное количество элементов. Такие массивы называют зубчатыми (англ. jagged array). Например:

Посмотрим код, реализующий такой массив. При объявлении двумерного массива необходимо задать количество элементов только для первой размерности — int[][] array = new int[4][]. Таким образом, мы указываем количество строк в массиве, но под каждую строку память не выделяем. Далее выделяем отдельно память под каждую строку массива. Например, строка с индексом ноль будет размером 1 — array[0] = new int[1]:
public class TwoDArrayExample2 {
public static void main(String[] args) {
int[][] array = new int[4][];
array[0] = new int[1];
array[1] = new int[2];
array[2] = new int[3];
array[3] = new int[4];
int value = 0;
for (int i = 0; i < array.length; i++) {
for (int j = 0; j < array[i].length; j++) {
array[i][j] = value++;
System.out.print(array[i][j] + " ");
}
System.out.println();
}
}
} Обратите внимание: границы циклов заданы не константами, а через array.length и array[i].length — так перебор работает для строк любой длины. У многомерного массива нет единого свойства «размер матрицы»: array.length — это количество строк, а длина каждой строки запрашивается отдельно. Подробный разбор длины массива смотрите в отдельном уроке «Длина массива в Java».
Инициализация при объявлении
Для многомерных массивов можно также использовать блок инициализации, если значения всех элементов заранее известны. Каждая отдельная строка заключается в фигурные скобки, а для перебора удобно применить вложенный цикл for-each:
public class TwoDArrayExample3 {
public static void main(String[] args) {
double[][] arrayTwoD = {
{0, 1, 2, 3},
{4, 5, 6, 7},
{8, 9, 10, 11},
{12, 13, 14, 15}
};
for (double[] arrayOneD : arrayTwoD) {
for (double element : arrayOneD) {
System.out.print(element + " ");
}
System.out.println();
}
}
} Целочисленные литералы при этом автоматически преобразуются к типу double, поэтому программа выведет 0.0 1.0 2.0 3.0 и так далее.
Часто задаваемые вопросы
Можно ли при создании массива указать только вторую размерность, например new int[][4]?
Нет, это ошибка компиляции. Первая (левая) размерность обязательна, потому что внешний массив ссылок должен быть создан сразу. Правильно: new int[4][] — а строки можно выделить позже.
Чем двумерный массив в Java отличается от матрицы в C/C++?
В C двумерный массив — сплошной блок памяти. В Java это массив ссылок на отдельные одномерные массивы: строки лежат в куче независимо друг от друга, могут иметь разную длину и даже быть null.
Как скопировать двумерный массив?
Метод clone() и Arrays.copyOf() копируют только внешний массив ссылок — обе копии будут разделять одни и те же строки. Для полной (глубокой) копии нужно скопировать каждую строку отдельно в цикле.
Как сравнить два многомерных массива?
Используйте Arrays.deepEquals(a, b). Метод Arrays.equals() сравнивает вложенные массивы по ссылкам, а не по содержимому, поэтому для многомерных массивов он вернёт false даже при одинаковых значениях.
Видео объяснение
Предпочитаете видеоформат? Посмотрите этот урок с примерами и объяснениями.
Комментарии