— Приветствую, Амиго!

— Здравствуй, Риша!

— Ты уже знаешь кое-что о массивах, даже задачи уже успел порешать, я надеюсь. Но ты знаешь далеко не всё. Вот например, ещё один интересный факт о массивах. Они — массивы — бывают не только одномерными (линейными), но и двумерными.

— Эмм… И что это значит?

— А это значит, что ячейки массива можно представить не только в виде столбца (или строки), но и в виде прямоугольной таблицы.

int[][]имя = new int[ширина][высота];

— Где имя — это имя переменной-массива, ширина — это ширина таблицы (в ячейках), а высота — это высота таблицы. Посмотри на пример:

int[][] data = new int[2][5];
data[1][1] = 5;
Создаем двумерный массив: два столбца и 5 строк.
В ячейку (1,1) записываем 5.

— Вот как это будет выглядеть в памяти:

— Кстати, для двумерных массивов можно тоже использовать быструю инициализацию:

// длины месяцев года поквартально
int[][] months = { {31, 28, 31}, {30, 31, 30}, {31, 31, 30}, {31, 30, 31} };

— Хм… интересно получается. Если представить что в первых внутренних скобках один элемент, в следующих второй… Выходит, двумерный массив — это как бы массив массивов?

— Какой же ты смышлёный ученик! Именно так. Первый его элемент — одномерный массив {31, 28, 31}, второй — {30, 31, 30}, и так далее. Но к этому мы вернёмся чуть позднее в этой лекции. А пока постарайся представить двумерный массив в виде таблицы со строками и столбцами, которые образуют ячейки на пересечении.

— Представил. Кстати, а для чего их вообще используют, эти двумерные массивы?

— Программисту часто может понадобиться двумерный массив. Если присмотреться, реализация практически любой настольной игры — это же готовый двумерный массив: «Шахматы», «Шашки», «Крестики-Нолики», «Морской бой»:

— Я понял! Игровое поле «Шахмат» или «Морского боя» просто идеально ложится на двумерные массивы!

— Да, только в качестве координат клеток нужно будет использовать только числа. Не «пешка e2-e4», а «пешка (5,2) -> (5,4)». Тебе как программисту будет даже проще.

Расположение элементов в массивах: (x,y) или (y,x)

— С созданием двумерных массивов возникает интересная дилемма. Когда мы создаем массив new int[2][5]; у нас таблица «две строки и 5 столбцов» или все-таки «два столбца и 5 строк»?

— То есть, не совсем понятно, задаём ли мы сначала «ширину», а потом «высоту»... или наоборот, сначала «высоту», а потом — «ширину»?

— Да, дилемма состоит именно в этом. И однозначного ответа нет.

— Что же делать?

— Для начала важно понять, как на самом деле наш двумерный массив хранится в памяти. Естественно, никаких таблиц в самой памяти компьютера нет: все байты памяти пронумерованы как 0, 1, 2, ... Это для нас таблица 2×5, а в памяти — просто 10 ячеек и всё. Без разделения на строки и столбцы.

— Это я понял. Как тогда определить, какая координата первая — ширина или высота?

— Рассмотрим первый вариант. Сначала ширина, потом высота. Аргумент в пользу такого подхода: все в школе учили математику, и что из пары координат сначала указывается «x» (то есть ширина), а затем «y» (высота). И это не просто школьный стандарт — это вообще стандарт в математике. Против царицы наук, как говорится, не попрешь.

— Так что? Раз не попрёшь, значит, сначала «ширина», а затем — «высота»?

— Есть интересный аргумент и в пользу гипотезы «сначала высота, затем ширина». Исходит этот аргумент из быстрой инициализации двумерных массивов. Ведь если мы захотим инициализировать наш массив, напишем этот код как:

// Важная матрица с данными
int[][] matrix = { {1, 2, 3, 4, 5}, {1, 2, 3, 4, 5} };

— И что с того?

— Ничего не заметил? А если так:

// Важная матрица с данными
int[][] matrix = {
  {1, 2, 3, 4, 5},
  {1, 2, 3, 4, 5}
};

— Если мы напишем наши данные в коде построчно, то получим таблицу, у которой 2 строки и 5 столбцов.

— Теперь вижу. 2 — это высота, и 5 — ширина… И как же тогда поступать?

— Тебе решать, как удобнее. Главное, чтобы все программисты, работающие над одним проектом, придерживались одного подхода.

— Если ты будешь работать над проектом, где много инициализированных двумерных массивов в коде, то скорее всего там все будут отталкиваться от быстрой инициализации данных и будет стандарт «высота»-«ширина».

— Если же ты попадешь в проект, где много математики и работают с координатами (например, работа с игровыми движками), там скорее всего будут придерживаться подхода «ширина»-«высота».

Устройство двумерных массивов

— Итак, ты помнишь, какую особенность структуры двумерных массивов ты подметил в начале лекции?

— Да, то, что двумерные массивы — это на самом деле массивы массивов!

— Совершенно верно. Другими словами, если в случае с обычным массивом «переменная-массив хранит ссылку на контейнер, который хранит элементы массива», то в случае с двумерными массивами у нас ситуация немного взрывоопаснее: переменная-двумерный-массив хранит ссылку на контейнер, который хранит ссылки на одномерные массивы. Это лучше один раз увидеть, чем сто раз попробовать объяснить:

Слева у нас «переменная-двумерный-массив», которая хранит ссылку на «объект-двумерный-массив». В середине — «объект двумерный массив», в ячейках которого хранятся ссылки на одномерные массивы — строки двумерного массива. Ну и справа ты можешь видеть четыре одномерных массива — строки нашего двумерного массива. Это то, как на самом деле устроены двумерные массивы.

— Здорово! Но что нам это даёт?

— Поскольку «контейнер контейнеров» хранит ссылки на «массивы-строки», мы можем очень быстро и просто менять строки местами. Чтобы получить доступ к «контейнеру контейнеров», нужно просто указать один индекс вместо двух. Пример:

int[][] data = new int[2][5];
int[] row1 = data[0];
int[] row2 = data[1];

— Посмотри на код ниже. С его помощью мы можем поменять строки местами:

// Важная матрица с данными
int[][] matrix = {
  {1, 2, 3, 4, 5},
  {5, 4, 3, 2, 1}
};

int[] tmp = matrix[0];
matrix[0] = matrix[1];
matrix[1] = tmp;
Двумерный массив





В matrix[0] у нас хранится ссылка на первую строку.
Меняем ссылки местами.

В итоге массив matrix выглядит так:
{
  {5, 4, 3, 2, 1},
  {1, 2, 3, 4, 5}
};

— Понял. Работает как обычный swap двух элементов.

— Так и есть. Ну а если ты обращаешься к ячейке двумерного массива, но после имени массива указываешь только один индекс, ты таким образом обратишься к контейнеру контейнеров, в ячейках которого хранятся ссылки на обычные одномерные массивы.

— Всё кажется логичным и понятным. Спасибо за лекцию, Риша!

— Пожалуйста. Применяй на практике с умом.

undefined
6
Задача
Java Syntax Pro, 6 уровень, 5 лекция
Недоступна
Таблица умножения
Проинициализируй массив MULTIPLICATION_TABLE значением new int[10][10], заполни его таблицей умножения и выведи в консоли в следующем виде: 1 2 3 4 … 2 4 6 8 … 3 6 9 12 … 4 8 12 16 … … Числа в строке разделены пробелом.