Многомерные и зубчатые массивы
До сих пор шкафчики стояли в один ряд. А ячейки камеры хранения на вокзале часто стоят стеной: несколько рядов друг над другом. Чтобы найти ячейку, нужны два номера — ряд и место в ряду. Так же устроены таблица, игровое поле и места в зрительном зале.
В C# такие данные хранят двумя способами:
- в прямоугольном массиве, где все ряды одной длины;
- в массиве массивов, где у каждого ряда своя длина.
Прямоугольный массив
Прямоугольный массив объявляют с запятой в квадратных скобках: int[,]. Такой массив называют многомерным: у него два измерения, ряды и столбцы.
int[,] grid = new int[2, 3];
grid[0, 1] = 5;
Console.WriteLine(grid[0, 1]); // 5
Console.WriteLine(grid[1, 0]); // 0
Console.WriteLine(grid.Length); // 6
Console.WriteLine(grid.GetLength(0)); // 2
Console.WriteLine(grid.GetLength(1)); // 3Давайте разберём по строкам:
new int[2, 3]создаёт два ряда по три элемента, и в каждом элементе лежит ноль.grid[0, 1] = 5кладёт5в ряд0, столбец1: сначала номер ряда, потом номер столбца.grid[1, 0]никто не менял, поэтому там0.grid.Length— число всех элементов:2 * 3, то есть6.grid.GetLength(0)— число рядов,2, аgrid.GetLength(1)— число столбцов,3.
Значения можно задать сразу, ряд за рядом в фигурных скобках. Перечень в квадратных скобках, как у обычного массива, здесь не подойдёт: компилятор сообщит об ошибке CS9174.
Обходят такой массив двумя вложенными циклами:
int[,] seats = { { 1, 2, 3 }, { 4, 5, 6 } };
for (int r = 0; r < seats.GetLength(0); r++)
{
int sum = 0;
for (int c = 0; c < seats.GetLength(1); c++)
{
sum += seats[r, c];
}
Console.WriteLine(sum);
}
// 6
// 15Внешний цикл перебирает ряды, а внутренний — столбцы одного ряда. Для каждого ряда программа печатает его сумму: 1 + 2 + 3 и 4 + 5 + 6.
Зубчатый массив
В массиве массивов каждый ряд — отдельный обычный массив, и ряды могут быть разной длины. Такой массив называют зубчатым: его правый край неровный, как зубья пилы. Тип int[][] читается как «массив, каждый элемент которого — массив int[]».
int[][] rows = [[1, 2], [3], [4, 5, 6]];
Console.WriteLine(rows.Length); // 3
Console.WriteLine(rows[2].Length); // 3
Console.WriteLine(rows[2][1]); // 5
foreach (int[] row in rows)
{
Console.WriteLine(string.Join(" ", row));
}
// 1 2
// 3
// 4 5 6Давайте разберём, что напечатала программа:
rows.Length— число рядов,3.rows[2]— третий ряд, сам по себе массив[4, 5, 6], и его длина тоже3.rows[2][1]берёт сначала ряд2, а в нём элемент1: это5.foreachпроходит по рядам,string.Joinсклеивает каждый ряд в строку, аConsole.WriteLineпечатает её.
Каждый элемент rows хранит ссылку на свой ряд, как любая переменная массива. Поэтому ряд можно заменить целиком: rows[1] = [7, 8, 9]; кладёт во второй элемент ссылку на новый ряд из трёх чисел.
Здесь картинка со стеной ячеек неточна. Ряды зубчатого массива не стоят вплотную друг к другу: каждый ряд — отдельный массив в своём месте памяти, а внешний массив хранит только ссылки на них.
Какой массив выбрать
Чем зубчатый массив отличается от многомерного — частый вопрос на собеседовании. Разница сводится к трём пунктам:
int[,] | int[][] | |
|---|---|---|
| Длина рядов | одинаковая | у каждого ряда своя |
| Обращение к элементу | grid[1, 2] | rows[1][2] |
| Устройство | один массив | массив ссылок на массивы-ряды |
Прямоугольный массив берут для таблицы с рядами одной длины, например для игрового поля. Зубчатый — когда длины разные, например для журнала, где у каждого ученика своё число оценок.
Частая ошибка
Число рядов прямоугольного массива взяли из Length:
int[,] grid = new int[2, 3];
for (int r = 0; r < grid.Length; r++) // ✗
{
Console.WriteLine(grid[r, 0]);
}Unhandled exception. System.IndexOutOfRangeException: Index was outside the bounds of the array.Программа печатает 0 дважды и останавливается с исключением. У прямоугольного массива Length — число всех элементов, 6, а рядов только 2, и при r, равном 2, ряда уже нет. Число рядов даёт grid.GetLength(0).
Главное
int[,]— прямоугольный массив: все ряды одной длины, а к элементу обращаются какgrid[ряд, столбец].- У
int[,]Length— число всех элементов, аGetLength(0)иGetLength(1)— число рядов и столбцов. int[][]— зубчатый массив, массив массивов: у каждого ряда своя длина, а к элементу обращаются какrows[ряд][номер].int[,]заполняют значениями в фигурных скобках, аint[][]— перечнем в квадратных:[[1, 2], [3]].