Многомерные массивы: таблицы и матрицы
Массивы массивов — создаём таблицы, работаем с матрицами и реализуем игру «Морской бой»
Что такое многомерные массивы?
До этого мы работали с одномерными массивами — линейными списками элементов. Но реальный мир часто устроен не линейно. Шахматная доска — это не список, а таблица 8×8. Расписание занятий — это таблица с днями недели и парами. Экран компьютера — это таблица пикселей шириной тысячи столбцов и высотой тысячи строк.
Двумерный массив (или массив массивов) — это структура данных, которая представляет собой таблицу с строками и столбцами. Каждый элемент массива определяется двумя индексами: номером строки и номером столбца.
Аналогия: Одномерный массив — это ряд шкафчиков в коридоре (номер 1, 2, 3...). Двумерный массив — это стеллаж с полками: чтобы найти нужную вещь, нужно сказать «3-я полка, 2-й шкафчик». Два числа определяют положение.
Визуально двумерный массив выглядит как таблица:
Столбец 0 Столбец 1 Столбец 2 Столбец 3
Строка 0 [ 1 ] [ 2 ] [ 3 ] [ 4 ]
Строка 1 [ 5 ] [ 6 ] [ 7 ] [ 8 ]
Строка 2 [ 9 ] [ 10 ] [ 11 ] [ 12 ]
Обратите внимание: сначала идёт индекс строки (горизонтально), потом — столбца (вертикально). Это как координаты: (строка, столбец).
Такие структуры используются повсюду:
- Таблицы данных — оценки учеников (строки — ученики, столбцы — предметы)
- Изображения — каждый пиксель задан координатами (x, y)
- Карты — игровые карты, географические координаты
- Матрицы — математические операции, 3D-графика
- Таблицы БД — строки и столбцы реляционных баз данных
Создание двумерных массивов
Способ 1: Указание размера
Создаём таблицу с фиксированным количеством строк и столбцов:
int[][] matrix = new int[3][4]; // 3 строки, 4 столбца
double[][] grid = new double[5][5]; // квадратная матрица 5×5
String[][] table = new String[2][3]; // таблица строк 2×3
Все элементы автоматически заполняются значениями по умолчанию: 0 для чисел, null для ссылок, false для boolean.
int[][] matrix = new int[2][3];
System.out.println(matrix[0][0]); // 0
System.out.println(matrix[0][1]); // 0
System.out.println(matrix[1][2]); // 0
Способ 2: Литерал (сразу с данными)
Можно сразу задать значения через вложенные фигурные скобки:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.println(matrix[0][0]); // 1
System.out.println(matrix[1][2]); // 6
System.out.println(matrix[2][1]); // 8
Каждая внутренняя строка {...} — это одна строка таблицы. Java автоматически определяет количество строк и столбцов.
Пример: Таблица умножения:
int[][] multiplication = {
{1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
{2, 4, 6, 8, 10, 12, 14, 16, 18, 20},
{3, 6, 9, 12, 15, 18, 21, 24, 27, 30}
};
System.out.println(multiplication[2][4]); // 15 (3 × 5)
Способ 3: new + инициализация
int[][] arr;
arr = new int[][] {
{10, 20},
{30, 40}
};
Как устроено в памяти: Двумерный массив в Java — это массив ссылок. Внешний массив содержит ссылки на внутренние массивы (строки). Каждый внутренний массив — это отдельный объект в памяти. Именно поэтому строки могут быть разной длины!
Доступ к элементам: два индекса
Чтобы обратиться к элементу двумерного массива, нужно указать оба индекса: matrix[строка][столбец]. Индексы, как и в одномерных массивах, начинаются с 0.
int[][] scores = {
{5, 4, 3}, // оценки Анны
{4, 5, 5}, // оценки Бориса
{3, 3, 4} // оценки Виктора
};
// Анна (строка 0), математика (столбец 0)
System.out.println("Оценка Анны по математике: " + scores[0][0]); // 5
// Борис (строка 1), физика (столбец 1)
System.out.println("Оценка Бориса по физике: " + scores[1][1]); // 5
// Виктор (строка 2), русский (столбец 2)
System.out.println("Оценка Виктора по русскому: " + scores[2][2]); // 4
Можно изменять элементы через индексы:
int[][] grid = new int[3][3];
grid[0][0] = 1;
grid[0][1] = 2;
grid[0][2] = 3;
grid[1][0] = 4;
grid[1][1] = 5;
grid[1][2] = 6;
grid[2][0] = 7;
grid[2][1] = 8;
grid[2][2] = 9;
System.out.println(grid[1][1]); // 5
Свойства двумерного массива:
int[][] arr = new int[3][5];
System.out.println(arr.length); // 3 — количество строк
System.out.println(arr[0].length); // 5 — количество столбцов в строке 0
System.out.println(arr[2].length); // 5 — количество столбцов в строке 2
Ошибка новичков: Путают порядок индексов. Помните: matrix[строка][столбец]. Первый индекс — строка (горизонталь), второй — столбец (вертикаль). Если написать наоборот — получите неверный элемент или ошибку.
Перебор двумерных массивов
Для обхода всех элементов двумерного массива используются вложенные циклы: внешний проходит по строкам, внутренний — по столбцам.
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[row].length; col++) {
System.out.print(matrix[row][col] + " ");
}
System.out.println();
}
// Вывод:
// 1 2 3
// 4 5 6
// 7 8 9
Как это работает пошагово:
- Внешний цикл: row = 0 → внутренний цикл перебирает все столбцы строки 0
- Внешний цикл: row = 1 → внутренний цикл перебирает все столбцы строки 1
- Внешний цикл: row = 2 → внутренний цикл перебирает все столбцы строки 2
Вариант с for-each (вложенными):
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
for (int[] row : matrix) { // row — это массив (строка)
for (int cell : row) { // cell — это элемент (ячейка)
System.out.print(cell + " ");
}
System.out.println();
}
// Вывод:
// 1 2 3
// 4 5 6
// 7 8 9
Пример: Подсчёт суммы всех элементов:
int[][] data = {
{10, 20, 30},
{40, 50, 60}
};
int total = 0;
for (int[] row : data) {
for (int val : row) {
total += val;
}
}
System.out.println("Сумма всех элементов: " + total); // 210
Пример: Поиск максимального элемента:
int[][] matrix = {
{3, 7, 2},
{9, 1, 5},
{4, 8, 6}
};
int max = matrix[0][0];
int maxRow = 0;
int maxCol = 0;
for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[row].length; col++) {
if (matrix[row][col] > max) {
max = matrix[row][col];
maxRow = row;
maxCol = col;
}
}
}
System.out.println("Максимум: " + max + " на позиции [" + maxRow + "][" + maxCol + "]");
// Вывод: Максимум: 9 на позиции [1][0]
Неравномерные (jagged) массивы
В Java строки двумерного массива могут быть разной длины! Это называется «зубчатый массив» (jagged array). Такая особенность связана с тем, как устроены массивы в памяти: внешний массив хранит ссылки на внутренние массивы, и каждый из них — отдельный объект.
int[][] jagged = new int[3][];
jagged[0] = new int[2]; // строка 0: 2 столбца
jagged[1] = new int[4]; // строка 1: 4 столбца
jagged[2] = new int[1]; // строка 2: 1 столбец
jagged[0][0] = 10;
jagged[0][1] = 20;
jagged[1][0] = 30;
jagged[1][1] = 40;
jagged[1][2] = 50;
jagged[1][3] = 60;
jagged[2][0] = 70;
System.out.println(jagged[0].length); // 2
System.out.println(jagged[1].length); // 4
System.out.println(jagged[2].length); // 1
Визуально:
jagged[0]: [10, 20]
jagged[1]: [30, 40, 50, 60]
jagged[2]: [70]
Литеральная запись зубчатого массива:
int[][] pyramid = {
{1},
{2, 3},
{4, 5, 6},
{7, 8, 9, 10}
};
for (int row = 0; row < pyramid.length; row++) {
for (int col = 0; col < pyramid[row].length; col++) {
System.out.print(pyramid[row][col] + " ");
}
System.out.println();
}
// Вывод:
// 1
// 2 3
// 4 5 6
// 7 8 9 10
Практический пример: Расписание — у разных групп разное количество пар:
String[][] schedule = {
{"Математика", "Физика", "Информатика"}, // группа А: 3 пары
{"Русский язык", "Литература"}, // группа Б: 2 пары
{"История", "Английский", "Химия", "Биология"} // группа В: 4 пары
};
for (int group = 0; group < schedule.length; group++) {
System.out.print("Группа " + (group + 1) + ": ");
for (int lesson = 0; lesson < schedule[group].length; lesson++) {
System.out.print(schedule[group][lesson]);
if (lesson < schedule[group].length - 1) {
System.out.print(", ");
}
}
System.out.println();
}
// Вывод:
// Группа 1: Математика, Физика, Информатика
// Группа 2: Русский язык, Литература
// Группа 3: История, Английский, Химия, Биология
Совет: При переборе зубчатого массива всегда используйте matrix[row].length для внутреннего цикла, а не фиксированное число. Это гарантирует, что вы обработаете все элементы независимо от длины строки.
Вывод двумерных массивов
Для красивого вывода двумерного массива используется метод Arrays.deepToString():
import java.util.Arrays;
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
System.out.println(Arrays.deepToString(matrix));
// Вывод: [[1, 2, 3], [4, 5, 6]]
Для форматированного вывода в виде таблицы используйте цикл:
int[][] matrix = {
{10, 20, 30},
{40, 50, 60},
{70, 80, 90}
};
for (int[] row : matrix) {
for (int val : row) {
System.out.printf("%4d", val);
}
System.out.println();
}
// Вывод:
// 10 20 30
// 40 50 60
// 70 80 90
Вывод с номерами строк и столбцов:
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
System.out.print(" ");
for (int col = 0; col < matrix[0].length; col++) {
System.out.printf(" [%d]", col);
}
System.out.println();
for (int row = 0; row < matrix.length; row++) {
System.out.printf("[%d] ", row);
for (int col = 0; col < matrix[row].length; col++) {
System.out.printf(" %3d ", matrix[row][col]);
}
System.out.println();
}
// Вывод:
// [0] [1] [2]
// [0] 1 2 3
// [1] 4 5 6
// [2] 7 8 9
Операции с матрицами
Сложение двух матриц
Чтобы сложить две матрицы, нужно просто прибавить элементы с соответствующими индексами. Матрицы должны быть одного размера!
import java.util.Arrays;
int[][] a = {
{1, 2, 3},
{4, 5, 6}
};
int[][] b = {
{10, 20, 30},
{40, 50, 60}
};
int[][] sum = new int[a.length][a[0].length];
for (int row = 0; row < a.length; row++) {
for (int col = 0; col < a[0].length; col++) {
sum[row][col] = a[row][col] + b[row][col];
}
}
System.out.println("A: " + Arrays.deepToString(a));
System.out.println("B: " + Arrays.deepToString(b));
System.out.println("A+B: " + Arrays.deepToString(sum));
// Вывод:
// A: [[1, 2, 3], [4, 5, 6]]
// B: [[10, 20, 30], [40, 50, 60]]
// A+B: [[11, 22, 33], [44, 55, 66]]
Умножение матрицы на число
import java.util.Arrays;
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
int scalar = 3;
int[][] result = new int[matrix.length][matrix[0].length];
for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[0].length; col++) {
result[row][col] = matrix[row][col] * scalar;
}
}
System.out.println(Arrays.deepToString(result));
// Вывод: [[3, 6, 9], [12, 15, 18]]
Умножение матриц
Умножение матриц — одна из ключевых операций в математике и программировании. Правило: элемент результата [i][j] равен сумме произведений элементов i-й строки первой матрицы на j-й столбец второй матрицы.
Формула: C[i][j] = Σ (A[i][k] * B[k][j]) для всех k
import java.util.Arrays;
int[][] A = {
{1, 2},
{3, 4}
};
int[][] B = {
{5, 6},
{7, 8}
};
int[][] C = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 2; k++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
System.out.println("A: " + Arrays.deepToString(A));
System.out.println("B: " + Arrays.deepToString(B));
System.out.println("A×B: " + Arrays.deepToString(C));
// Вывод:
// A: [[1, 2], [3, 4]]
// B: [[5, 6], [7, 8]]
// A×B: [[19, 22], [43, 50]]
Разберём C[0][0] = 19:
- A[0][0] × B[0][0] = 1 × 5 = 5
- A[0][1] × B[1][0] = 2 × 7 = 14
- 5 + 14 = 19
Разберём C[1][0] = 43:
- A[1][0] × B[0][0] = 3 × 5 = 15
- A[1][1] × B[1][0] = 4 × 7 = 28
- 15 + 28 = 43
Транспонирование матрицы
Транспонирование — замена строк на столбцы и наоборот:
import java.util.Arrays;
int[][] matrix = {
{1, 2, 3},
{4, 5, 6}
};
int[][] transposed = new int[matrix[0].length][matrix.length];
for (int row = 0; row < matrix.length; row++) {
for (int col = 0; col < matrix[row].length; col++) {
transposed[col][row] = matrix[row][col];
}
}
System.out.println("Оригинал: " + Arrays.deepToString(matrix));
System.out.println("Транспонированная: " + Arrays.deepToString(transposed));
// Вывод:
// Оригинал: [[1, 2, 3], [4, 5, 6]]
// Транспонированная: [[1, 4], [2, 5], [3, 6]]
Практический проект: Морской бой (упрощённый)
Давайте применим знания о двумерных массивах на практике, создав упрощённую игру «Морской бой». Это отличная задача для закрепления навыков работы с таблицами.
Шаг 1: Создание игрового поля
Поле — это квадрат 10×10. Используем символы:
'~'— вода (пустая клетка)'S'— корабль'X'— попадание'O'— промах
char[][] field = new char[10][10];
for (int row = 0; row < 10; row++) {
for (int col = 0; col < 10; col++) {
field[row][col] = '~';
}
}
Шаг 2: Размещение кораблей
void placeShip(char[][] field, int row, int col, int size, boolean horizontal) {
for (int i = 0; i < size; i++) {
if (horizontal) {
field[row][col + i] = 'S';
} else {
field[row + i][col] = 'S';
}
}
}
placeShip(field, 0, 0, 3, true); // горизонтальный корабль длины 3
placeShip(field, 2, 5, 2, false); // вертикальный корабль длины 2
placeShip(field, 5, 1, 4, true); // горизонтальный корабль длины 4
Шаг 3: Вывод поля
void printField(char[][] field) {
System.out.print(" ");
for (int col = 0; col < field[0].length; col++) {
System.out.print(col + " ");
}
System.out.println();
for (int row = 0; row < field.length; row++) {
System.out.print((char)('A' + row) + " ");
for (int col = 0; col < field[row].length; col++) {
System.out.print(field[row][col] + " ");
}
System.out.println();
}
}
printField(field);
// Вывод:
// 0 1 2 3 4 5 6 7 8 9
// A S S S ~ ~ ~ ~ ~ ~
// B ~ ~ ~ ~ ~ ~ ~ ~ ~
// C ~ ~ ~ ~ ~ S S ~ ~
// D ~ ~ ~ ~ ~ S ~ ~ ~
// E ~ ~ ~ ~ ~ ~ ~ ~ ~
// F ~ S S S S ~ ~ ~ ~
// G ~ ~ ~ ~ ~ ~ ~ ~ ~
// H ~ ~ ~ ~ ~ ~ ~ ~ ~
// I ~ ~ ~ ~ ~ ~ ~ ~ ~
// J ~ ~ ~ ~ ~ ~ ~ ~ ~
Шаг 4: Обработка выстрела
String shoot(char[][] field, int row, int col) {
if (field[row][col] == 'S') {
field[row][col] = 'X';
return "Попадание!";
} else if (field[row][col] == '~') {
field[row][col] = 'O';
return "Промах!";
} else {
return "Вы уже стреляли в эту клетку!";
}
}
System.out.println(shoot(field, 0, 0)); // Попадание!
System.out.println(shoot(field, 1, 1)); // Промах!
System.out.println(shoot(field, 0, 0)); // Вы уже стреляли!
Шаг 5: Проверка победы
boolean allShipsDestroyed(char[][] field) {
for (int row = 0; row < field.length; row++) {
for (int col = 0; col < field[row].length; col++) {
if (field[row][col] == 'S') {
return false;
}
}
}
return true;
}
System.out.println("Все корабли потоплены: " + allShipsDestroyed(field)); // false
placeShip(field, 5, 1, 4, true);
for (int i = 1; i <= 4; i++) {
field[5][i] = 'X';
}
System.out.println("Все корабли потоплены: " + allShipsDestroyed(field)); // true
Практические задачи
Задача 1: Повернуть матрицу на 90° по часовой стрелке
import java.util.Arrays;
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int n = matrix.length;
int[][] rotated = new int[n][n];
for (int row = 0; row < n; row++) {
for (int col = 0; col < n; col++) {
rotated[col][n - 1 - row] = matrix[row][col];
}
}
System.out.println("Исходная: " + Arrays.deepToString(matrix));
System.out.println("Повёрнутая: " + Arrays.deepToString(rotated));
// Вывод:
// Исходная: [[1, 2, 3], [4, 5, 6], [7, 8, 9]]
// Повёрнутая: [[7, 4, 1], [8, 5, 2], [9, 6, 3]]
Задача 2: Найти сумму по диагоналям
import java.util.Arrays;
int[][] matrix = {
{1, 2, 3},
{4, 5, 6},
{7, 8, 9}
};
int mainDiagSum = 0;
int antiDiagSum = 0;
for (int i = 0; i < matrix.length; i++) {
mainDiagSum += matrix[i][i];
antiDiagSum += matrix[i][matrix.length - 1 - i];
}
System.out.println("Главная диагональ: " + mainDiagSum); // 15 (1+5+9)
System.out.println("Побочная диагональ: " + antiDiagSum); // 15 (3+5+7)
Задача 3: Подсчитать количество вхождений числа
import java.util.Arrays;
int[][] matrix = {
{1, 2, 3, 2},
{4, 2, 6, 2},
{7, 8, 2, 10}
};
int target = 2;
int count = 0;
for (int[] row : matrix) {
for (int val : row) {
if (val == target) {
count++;
}
}
}
System.out.println("Число " + target + " встречается " + count + " раз");
// Вывод: Число 2 встречается 4 раз
Задача 4: Создать единичную матрицу
import java.util.Arrays;
int n = 5;
int[][] identity = new int[n][n];
for (int i = 0; i < n; i++) {
identity[i][i] = 1;
}
System.out.println(Arrays.deepToString(identity));
// Вывод: [[1, 0, 0, 0, 0], [0, 1, 0, 0, 0], [0, 0, 1, 0, 0], [0, 0, 0, 1, 0], [0, 0, 0, 0, 1]]
Задача 5: Найти строку с наибольшей суммой
import java.util.Arrays;
int[][] table = {
{10, 20, 30},
{50, 5, 10},
{25, 25, 25}
};
int maxSum = 0;
int maxRowIndex = 0;
for (int row = 0; row < table.length; row++) {
int sum = 0;
for (int val : table[row]) {
sum += val;
}
if (sum > maxSum) {
maxSum = sum;
maxRowIndex = row;
}
}
System.out.println("Строка " + maxRowIndex + " имеет максимальную сумму: " + maxSum);
// Вывод: Строка 1 имеет максимальную сумму: 65
Массивы с бо́льшей размерностью
В Java поддерживается любое количество измерений. Трёхмерный массив — это «куб» данных, четырёхмерный — и так далее. На практике чаще всего используются одномерные и двумерные, но полезно знать о возможности большей размерности.
int[][][] cube = new int[3][4][5]; // 3 слоя, 4 строки, 5 столбцов
cube[0][0][0] = 1;
cube[1][2][3] = 42;
cube[2][3][4] = 100;
System.out.println(cube[0][0][0]); // 1
System.out.println(cube[1][2][3]); // 42
System.out.println(cube[2][3][4]); // 100
Применение трёхмерных массивов:
- 3D-графика — трёхмерные модели (x, y, z)
- Видео — трёхмерный массив (кадр, строка, столбец)
- Временные ряды данных — (измерение, день, час)
- Игровые карты с уровнями — (уровень, строка, столбец)
Совет: Если размерность массива превышает 3, его становится очень сложно визуализировать и отлаживать. На практике для сложных структур данных используют объектные классы (класс Point, класс Cell, класс Layer) вместо многомерных массивов.
Типичные ошибки при работе с двумерными массивами
Ошибка 1: Путаница строк и столбцов
int[][] arr = new int[3][5]; // 3 строки, 5 столбцов
// ❌ Неправильно: arr[5][3] — IndexError!
// arr[столбец][строка]
// ✅ Правильно: arr[строка][столбец]
arr[0][0] = 1; // строка 0, столбец 0
arr[2][4] = 2; // строка 2, столбец 4
Ошибка 2: Фиксированная длина столбцов для зубчатого массива
int[][] jagged = new int[3][];
jagged[0] = new int[2];
jagged[1] = new int[5];
// ❌ Ошибка: предполагаем все строки длины 2
for (int col = 0; col < 2; col++) {
System.out.println(jagged[1][col]); // OK для col 0,1
}
// ❌ А если попробовать col = 2? Для jagged[1] это ОК, но не для jagged[0]
// ✅ Правильно:
for (int col = 0; col < jagged[1].length; col++) {
System.out.println(jagged[1][col]);
}
Ошибка 3: Забыть импорт для deepToString
int[][] arr = {{1, 2}, {3, 4}};
// ❌ Без импорта:
System.out.println(Arrays.deepToString(arr)); // Error
// ✅ С импортом:
import java.util.Arrays;
System.out.println(Arrays.deepToString(arr)); // [[1, 2], [3, 4]]
Ошибка 4: Сравнение двумерных массивов через equals
int[][] a = {{1, 2}, {3, 4}};
int[][] b = {{1, 2}, {3, 4}};
// ❌ Неправильно:
System.out.println(a.equals(b)); // false
// ✅ Правильно:
System.out.println(Arrays.deepEquals(a, b)); // true
Детальное изучение умножения матриц
Умножение матриц — одна из фундаментальных операций в линейной алгебре, которая используется повсюду: от компьютерной графики до машинного обучения. Давайте разберём её подробнее.
Правило размерностей: Чтобы умножить матрицу A размером m×n на матрицу B размером n×p, нужно чтобы количество столбцов A совпадало с количеством строк B. Результат будет иметь размерность m×p.
import java.util.Arrays;
// A: 2×3, B: 3×2 → результат: 2×2
int[][] A = {
{1, 2, 3},
{4, 5, 6}
};
int[][] B = {
{7, 8},
{9, 10},
{11, 12}
};
int[][] C = new int[2][2];
for (int i = 0; i < 2; i++) {
for (int j = 0; j < 2; j++) {
for (int k = 0; k < 3; k++) {
C[i][j] += A[i][k] * B[k][j];
}
}
}
System.out.println("A (2×3): " + Arrays.deepToString(A));
System.out.println("B (3×2): " + Arrays.deepToString(B));
System.out.println("A×B (2×2): " + Arrays.deepToString(C));
// Вывод:
// A (2×3): [[1, 2, 3], [4, 5, 6]]
// B (3×2): [[7, 8], [9, 10], [11, 12]]
// A×B (2×2): [[58, 64], [139, 154]]
Разберём C[0][0] = 58:
- A[0][0] × B[0][0] = 1 × 7 = 7
- A[0][1] × B[1][0] = 2 × 9 = 18
- A[0][2] × B[2][0] = 3 × 11 = 33
- 7 + 18 + 33 = 58
Разберём C[0][1] = 64:
- A[0][0] × B[0][1] = 1 × 8 = 8
- A[0][1] × B[1][1] = 2 × 10 = 20
- A[0][2] × B[2][1] = 3 × 12 = 36
- 8 + 20 + 36 = 64
Важно: Умножение матриц НЕ коммутативно: A×B ≠ B×A в общем случае. Порядок сомножителей имеет значение! Это отличается от умножения обычных чисел.
Проверка: B×A — невозможно, потому что B имеет размерность 3×2, а A — 2×3. Количество столбцов B (2) не равно количеству строк A (2)... Подождите, 2=2, так что B×A возможно! Результат будет 3×3:
int[][] BA = new int[3][3];
for (int i = 0; i < 3; i++) {
for (int j = 0; j < 3; j++) {
for (int k = 0; k < 2; k++) {
BA[i][j] += B[i][k] * A[k][j];
}
}
}
System.out.println("B×A: " + Arrays.deepToString(BA));
// B×A: [[39, 54, 69], [49, 68, 87], [59, 82, 105]]
Визуализация данных с помощью двумерных массивов
Двумерные массивы отлично подходят для визуализации данных. Давайте создадим несколько примеров.
Пример 1: Гистограмма (горизонтальные бары)
int[] data = {5, 12, 8, 15, 3, 10, 7};
for (int i = 0; i < data.length; i++) {
System.out.printf("Позиция %d: ", i);
for (int j = 0; j < data[i]; j++) {
System.out.print("█");
}
System.out.println(" (" + data[i] + ")");
}
// Вывод:
// Позиция 0: █████ (5)
// Позиция 1: ████████████ (12)
// Позиция 2: ████████ (8)
// Позиция 3: ███████████████ (15)
// Позиция 4: ███ (3)
// Позиция 5: ██████████ (10)
// Позиция 6: ███████ (7)
Пример 2: Таблица умножения
int n = 10;
int[][] table = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
table[i][j] = (i + 1) * (j + 1);
}
}
System.out.print(" ");
for (int j = 1; j <= n; j++) {
System.out.printf("%4d", j);
}
System.out.println();
for (int i = 0; i < n; i++) {
System.out.printf("%2d |", i + 1);
for (int j = 0; j < n; j++) {
System.out.printf("%4d", table[i][j]);
}
System.out.println();
}
Пример 3: Карта расстояний (матрица попарных расстояний)
String[] cities = {"Москва", "СПб", "Новосибирск", "Екатеринбург"};
int[][] distances = {
{0, 650, 3350, 1770},
{650, 0, 3500, 2100},
{3350, 3500, 0, 1800},
{1770, 2100, 1800, 0}
};
System.out.println("Матрица расстояний (км):");
System.out.print(" ");
for (String city : cities) {
System.out.printf("%12s", city);
}
System.out.println();
for (int i = 0; i < cities.length; i++) {
System.out.printf("%10s", cities[i]);
for (int j = 0; j < cities.length; j++) {
System.out.printf("%12d", distances[i][j]);
}
System.out.println();
}
Пример 4: Тепловая карта оценок
String[] students = {"Анна", "Борис", "Виктор", "Галина"};
String[] subjects = {"Математика", "Физика", "Информатика"};
int[][] grades = {
{5, 4, 5},
{4, 5, 4},
{3, 4, 5},
{5, 5, 5}
};
System.out.print(" ");
for (String sub : subjects) {
System.out.printf("%12s", sub);
}
System.out.println();
for (int i = 0; i < students.length; i++) {
System.out.printf("%10s ", students[i]);
for (int j = 0; j < subjects.length; j++) {
String bar = "";
for (int k = 0; k < grades[i][j]; k++) {
bar += "●";
}
System.out.printf("%12s", bar);
}
System.out.println();
}
Практика: генерация простого лабиринта
Двумерный массив — идеальная структура для представления лабиринта. Давайте создадим простой лабиринт, где 0 — проход, а 1 — стена.
import java.util.Arrays;
int[][] maze = {
{1, 1, 1, 1, 1, 1, 1},
{1, 0, 0, 0, 1, 0, 1},
{1, 0, 1, 0, 1, 0, 1},
{1, 0, 1, 0, 0, 0, 1},
{1, 0, 1, 1, 1, 0, 1},
{1, 0, 0, 0, 0, 0, 1},
{1, 1, 1, 1, 1, 1, 1}
};
for (int row = 0; row < maze.length; row++) {
for (int col = 0; col < maze[row].length; col++) {
if (maze[row][col] == 1) {
System.out.print("██");
} else {
System.out.print(" ");
}
}
System.out.println();
}
// Вывод:
// ██████████████
// ██ ██
// ██ ██ ██
// ██ ██ ██
// ██ ██████ ██
// ██ ██
// ██████████████
Пример: Поиск пути в лабиринте (упрощённый BFS):
import java.util.Arrays;
int[][] maze = {
{1, 1, 1, 1, 1},
{1, 0, 0, 0, 1},
{1, 0, 1, 0, 1},
{1, 0, 0, 0, 1},
{1, 1, 1, 1, 1}
};
int startRow = 1, startCol = 1;
int endRow = 3, endCol = 3;
int[][] visited = new int[maze.length][maze[0].length];
boolean found = findPath(maze, visited, startRow, startCol, endRow, endCol);
if (found) {
System.out.println("Путь найден!");
for (int[] row : visited) {
System.out.println(Arrays.toString(row));
}
} else {
System.out.println("Путь не найден!");
}
static boolean findPath(int[][] maze, int[][] visited, int row, int col, int endRow, int endCol) {
if (row == endRow && col == endCol) {
visited[row][col] = 1;
return true;
}
if (row < 0 || row >= maze.length || col < 0 || col >= maze[0].length) return false;
if (maze[row][col] == 1 || visited[row][col] == 1) return false;
visited[row][col] = 1;
if (findPath(maze, visited, row + 1, col, endRow, endCol)) return true;
if (findPath(maze, visited, row, col + 1, endRow, endCol)) return true;
if (findPath(maze, visited, row - 1, col, endRow, endCol)) return true;
if (findPath(maze, visited, row, col - 1, endRow, endCol)) return true;
visited[row][col] = 0;
return false;
}
Итоги урока
- Двумерный массив — это таблица с индексами [строка][столбец]
- Создание:
new int[строки][столбцы]или{{...}, {...}} - Количество строк:
arr.length, столбцов:arr[0].length - Перебор — два вложенных цикла: внешний по строкам, внутренний по столбцам
- Зубчатые (jagged) массивы — строки разной длины
- Операции: сложение, умножение на число, умножение матриц, транспонирование
- Умножение матриц: C[i][j] = Σ A[i][k] × B[k][j], требует 3 вложенных цикла
- Вывод:
Arrays.deepToString()для краткой формы, циклы — для красивой таблицы - Сравнение:
Arrays.deepEquals()вместоequals() - Практическое применение: Морской бой, таблицы данных, лабиринты, визуализация
Следующий урок: Аргументы командной строки: String[] args
Тест: Многомерные массивы
8 вопросов