Перейти к содержимому
Теория · Массивы · урок 9 из 9

Многомерные и зубчатые массивы

До сих пор шкафчики стояли в один ряд. А ячейки камеры хранения на вокзале часто стоят стеной: несколько рядов друг над другом. Чтобы найти ячейку, нужны два номера — ряд и место в ряду. Так же устроены таблица, игровое поле и места в зрительном зале.

В C# такие данные хранят двумя способами:

  • в прямоугольном массиве, где все ряды одной длины;
  • в массиве массивов, где у каждого ряда своя длина.

Прямоугольный массив

Прямоугольный массив объявляют с запятой в квадратных скобках: int[,]. Такой массив называют многомерным: у него два измерения, ряды и столбцы.

C#
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

Давайте разберём по строкам:

  1. new int[2, 3] создаёт два ряда по три элемента, и в каждом элементе лежит ноль.
  2. grid[0, 1] = 5 кладёт 5 в ряд 0, столбец 1: сначала номер ряда, потом номер столбца.
  3. grid[1, 0] никто не менял, поэтому там 0.
  4. grid.Length — число всех элементов: 2 * 3, то есть 6.
  5. grid.GetLength(0) — число рядов, 2, а grid.GetLength(1) — число столбцов, 3.

Значения можно задать сразу, ряд за рядом в фигурных скобках. Перечень в квадратных скобках, как у обычного массива, здесь не подойдёт: компилятор сообщит об ошибке CS9174.

Обходят такой массив двумя вложенными циклами:

C#
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[]».

C#
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

Давайте разберём, что напечатала программа:

  1. rows.Length — число рядов, 3.
  2. rows[2] — третий ряд, сам по себе массив [4, 5, 6], и его длина тоже 3.
  3. rows[2][1] берёт сначала ряд 2, а в нём элемент 1: это 5.
  4. foreach проходит по рядам, string.Join склеивает каждый ряд в строку, а Console.WriteLine печатает её.

Каждый элемент rows хранит ссылку на свой ряд, как любая переменная массива. Поэтому ряд можно заменить целиком: rows[1] = [7, 8, 9]; кладёт во второй элемент ссылку на новый ряд из трёх чисел.

Здесь картинка со стеной ячеек неточна. Ряды зубчатого массива не стоят вплотную друг к другу: каждый ряд — отдельный массив в своём месте памяти, а внешний массив хранит только ссылки на них.

Какой массив выбрать

Чем зубчатый массив отличается от многомерного — частый вопрос на собеседовании. Разница сводится к трём пунктам:

int[,]int[][]
Длина рядоводинаковаяу каждого ряда своя
Обращение к элементуgrid[1, 2]rows[1][2]
Устройствоодин массивмассив ссылок на массивы-ряды

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

Частая ошибка

Число рядов прямоугольного массива взяли из Length:

C#
int[,] grid = new int[2, 3];
for (int r = 0; r < grid.Length; r++) // ✗
{
    Console.WriteLine(grid[r, 0]);
}
TEXT
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]].