1. Зубчатые массивы отличаются от двумерных
Вот мы и дошли до темы, которую многие называют «зубчатыми массивами» — по-английски jagged arrays. В отличие от двумерных массивов, зубчатые массивы позволяют хранить колонки разной длины. Это как если бы у вас был комплекс зданий, где у каждого здания своё количество квартир — в одном здании 5 квартир, в другом 20, а в третьем — всего одна.
Зубчатый массив — это массив, каждый элемент которого является массивом, и при этом вложенные массивы (их ещё называют «подмассивы») могут иметь разную длину.
Главное отличие:
- В двумерном массиве у каждой «строки» (и у каждого «столбца») одинаковое количество элементов. Пример: int[][] grid = new int[3][5]; — у нас всегда 3 строки по 5 элементов.
- В зубчатом массиве каждая строка может быть разной длины! Пример: int[][] jagged = new int[3][]; — и только потом мы каждую строку (подмассив) инициализируем по‑своему.
Вот как это выглядит визуально:
Двумерный массив (3x3):
┌───┬───┬───┐
│ 1 │ 2 │ 3 │
├───┼───┼───┤
│ 4 │ 5 │ 6 │
├───┼───┼───┤
│ 7 │ 8 │ 9 │
└───┴───┴───┘
Зубчатый массив (разные длины):
┌───┬───┐
│ 1 │ 2 │
├───┼───┼───┬───┐
│ 3 │ 4 │ 5 │ 6 │
├───┼───┴───┴───┘
│ 7 │
└───┘
2. Синтаксис объявления и инициализации зубчатого массива
Объявление зубчатого массива не страшнее, чем объявления предыдущих типов! Пугаться двойных квадратных скобок не стоит:
int[][] jaggedArray = new int[3][];
Это значит, что у нас есть массив из 3 элементов, и каждый из них — это тоже массив int-ов. Но пока что внутренние массивы не созданы! Для лучшего понимания давайте разберём это подробнее.
Пошаговая инициализация зубчатого массива
Шаг 1 — создание основного (внешнего) массива:
int[][] jaggedArray = new int[3][];
Теперь у нас есть 3 «строки», но все они пока равны null.
Шаг 2 — создание и заполнение внутренних массивов (подмассивов):
Например, пусть первая строка будет длиной 2, вторая — 4, третья — 3:
jaggedArray[0] = new int[2]; // 2 элемента в первой строке
jaggedArray[1] = new int[4]; // 4 элемента во второй строке
jaggedArray[2] = new int[3]; // 3 элемента в третьей строке
Шаг 3 — заполнение значениями:
Внутренние массивы — это обычные массивы! Например:
jaggedArray[0][0] = 1;
jaggedArray[0][1] = 2;
jaggedArray[1][0] = 3;
jaggedArray[1][1] = 4;
jaggedArray[1][2] = 5;
jaggedArray[1][3] = 6;
jaggedArray[2][0] = 7;
jaggedArray[2][1] = 8;
jaggedArray[2][2] = 9;
Краткая инициализация зубчатого массива
Можно создать и заполнить зубчатый массив сразу, если вы знаете значения заранее:
int[][] jaggedArray = new int[][]
{
new int[] { 1, 2 },
new int[] { 3, 4, 5, 6 },
new int[] { 7, 8, 9 }
};
Или чуть короче, опуская тип внутренних массивов:
int[][] jaggedArray =
{
{ 1, 2 },
{ 3, 4, 5, 6 },
{ 7, 8, 9 }
};
3. Перебор и работа с зубчатыми массивами
Перебирать зубчатый массив — не сложнее, чем двумерный, но теперь внешний цикл идёт по строкам, а внутренний — по элементам строки (которые могут иметь разную длину):
for (int i = 0; i < jaggedArray.length; i++)
{
System.out.println("Строка " + i + ":");
for (int j = 0; j < jaggedArray[i].length; j++)
{
System.out.print(jaggedArray[i][j] + " ");
}
System.out.println();
}
Итог на экране:
Строка 0:
1 2
Строка 1:
3 4 5 6
Строка 2:
7 8 9
Можно использовать for-each, чтобы не думать об индексах:
for (int[] row : jaggedArray)
{
for (int value : row)
{
System.out.print(value + " ");
}
System.out.println();
}
4. Типичные сценарии применения зубчатых массивов
Когда зубчатый массив может быть полезнее двумерного?
- Если вы храните для каждого пользователя разное количество каких-то данных: оценки по предметам, покупки, комментарии и так далее.
- Если ваши данные имеют треугольную или ступенчатую структуру (например, для вывода пирамидок, треугольников Паскаля и др.).
- Если хотите экономить память: в двумерном массиве все строки фиксированы, а в зубчатом — только нужное количество элементов.
Пример из жизни: менеджер оценок студентов
Предположим, у нас есть три студента, и вот их оценки за разные задания по математике:
| Студент | Оценки |
|---|---|
| 0 | 5, 4 |
| 1 | 3, 4, 4 |
| 2 | 5 |
Объявим такой массив:
int[][] studentMarks = new int[3][];
studentMarks[0] = new int[] { 5, 4 }; // Первый студент — 2 оценки
studentMarks[1] = new int[] { 3, 4, 4 }; // Второй студент — 3 оценки
studentMarks[2] = new int[] { 5 }; // Третий студент — 1 оценка
Выведем оценки каждого студента:
for (int i = 0; i < studentMarks.length; i++)
{
System.out.print("Студент " + i + ": ");
for (int j = 0; j < studentMarks[i].length; j++)
{
System.out.print(studentMarks[i][j] + " ");
}
System.out.println();
}
Использование зубчатых массивов с другими типами
Зубчатым может быть массив чего угодно: строк, массивов других массивов (глубже!), даже ваших собственных объектов.
Пример: массив строк
String[][] groups = {
{ "Иван", "Пётр" },
{ "Мария", "Алексей", "Сергей" },
{ "Василиса" }
};
5. Трёхмерные и многомерные массивы
И ещё один интересный факт о массивах, о котором вы, наверное, уже догадываетесь. Если можно сделать двумерный массив, значит, можно сделать и трёхмерный?
Да, можно создать массив любой размерности. Такие массивы называют многомерными.
Как объявлять многомерные массивы
Достаточно просто перечислить нужное количество размеров через скобки:
int[][][] cube = new int[2][3][4]; // 2 "слоя", 3 строки, 4 столбца
cube[0][1][2] = 99;
Здесь у нас трёхмерный массив:
- 2 элемента по первой координате,
- 3 — по второй,
- 4 — по третьей.
Такой массив — это один большой «куб» данных, упакованный последовательно.
Перебор трёхмерного массива
Доступ к элементу осуществляется по всем индексам сразу:
for (int i = 0; i < cube.length; i++)
{
for (int j = 0; j < cube[i].length; j++)
{
for (int k = 0; k < cube[i][j].length; k++)
{
System.out.print(cube[i][j][k] + " ");
}
System.out.println();
}
System.out.println("---");
}
- Индексы нумеруются с нуля, как всегда в Java.
- Всего в таком массиве будет 2 × 3 × 4 = 24 элемента.
Практические примеры многомерных массивов
- 2D — таблицы, шахматные доски, изображения.
- 3D — «кубики» в компьютерной графике, данные для научных расчётов (например, температура в разных точках пространства и времени).
- 4D и выше — редко используются, но встречаются в продвинутой математике, симуляциях, машинном обучении и пр.
6. Типичные ошибки при работе с многомерными массивами
Ошибка №1: Выход за границы массива
Самая популярная ошибка — попытка обратиться к несуществующему элементу, например:
int[][] arr = new int[2][3];
arr[2][0] = 5; // Ошибка! Нет строки с индексом 2 (есть только 0 и 1)
arr[0][3] = 7; // Ошибка! Нет столбца с индексом 3 (есть только 0, 1, 2)
При таком обращении программа выбросит ArrayIndexOutOfBoundsException. Всегда проверяйте, что индексы в допустимых пределах: от 0 до длина - 1.
Ошибка №2: Неинициализированные строки в зубчатом массиве
Если создать зубчатый массив и забыть инициализировать внутренние массивы, при попытке обращения будет NullPointerException:
int[][] jagged = new int[3][];
jagged[0][0] = 5; // Ошибка! jagged[0] == null
Сначала нужно создать внутренний массив: jagged[0] = new int[2];
Ошибка №3: Неверное использование длины массива
Путают matrix.length (количество строк) и matrix[0].length (количество столбцов). Особенно часто — при копировании, переборе, суммировании по столбцам.
Ошибка №4: Предположение, что все строки одинаковой длины
В зубчатых массивах строки могут быть разной длины! Если вы пишете matrix[i][j], убедитесь, что j < matrix[i].length.
Ошибка №5: Путаница в порядке индексов
Иногда путают, что первым идёт строка, а потом столбец: matrix[строка][столбец]. Не наоборот!
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ