1. Двовимірні масиви

Ще один цікавий факт про масиви: масиви бувають не лише лінійними, а й двовимірними.

І що це означає, запитаєте ви?

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

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} };

Є дуже багато ситуацій, коли вам, як програмісту, може знадобитися двовимірний масив. Реалізація майже будь-якої настільної гри — це ж готовий двовимірний масив: «Шахи», «Шашки», «Хрестики-нулики», «Морський бій»:

Двовимірні масиви 2

Ігрове поле «Шахів» або «Морського бою» просто ідеально лягає на двовимірні масиви, тільки координати клітин треба буде позначати лише числами. Не «пішак e2 -> e4», а «пішак (4,1) -> (4,3)». Вам, як програмісту, буде навіть легше.


2. Розташування елементів у масивах: (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 стовпців.

Підсумки

Ну що тут скажеш? Як вам зручніше — вирішуєте ви. Головне, щоб усі програмісти, які працюють над одним проєктом, дотримувались однакового підходу.

Якщо ви працюватимете над проєктом, де в коді багато ініціалізованих двовимірних масивів , то, найімовірніше, там усі будуть використовувати швидку ініціалізацію даних і буде стандарт «висота»–«ширина».

Якщо ж вам пощастить потрапити в проєкт, де багато математики та працюють із координатами (наприклад, робота з ігровими рушіями), там, найімовірніше, будуть дотримуватися підходу «ширина»–«висота».


3. Будова двовимірних масивів

А зараз ви дізнаєтеся, як насправді влаштовано двовимірні масиви. Готові?

Двовимірні масиви — це насправді масиви масивів!

Інакше кажучи, якщо у випадку зі звичайним масивом «змінна-масив зберігає посилання на контейнер, в якому зберігаються елементи масиву», то у випадку з двовимірними масивами маємо дещо вибухонебезпечнішу ситуацію: змінна-двовимірний масив зберігає посилання на контейнер, в якому зберігаються посилання на одновимірні масиви. Це краще один раз побачити, ніж сто разів спробувати пояснити:

Ліворуч у нас «змінна-двовимірний-масив», яка зберігає посилання на «об'єкт-двовимірний масив». У центрі маємо «об'єкт-двовимірний масив»: у його комірках зберігаються посилання на одновимірні масиви — рядки двовимірного масиву. Ну й праворуч ви бачите чотири одновимірних масиви — рядки нашого двовимірного масиву.

Отак насправді влаштовано двовимірні масиви. І такий підхід дає Java-програмісту кілька переваг:

По-перше, оскільки «контейнер контейнерів» зберігає посилання на «масиви-рядки», ми можемо дуже швидко й просто міняти рядки місцями. Щоб отримати доступ до «контейнера контейнерів», треба просто вказати один індекс замість двох. Приклад:

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}
};

Якщо ви звертаєтеся до комірки двовимірного масиву, але після імені масиву вказуєте тільки один індекс, ви таким чином звернетеся до контейнера контейнерів, у комірках якого зберігаються посилання на звичайні одновимірні масиви.