$ sudo teach IT
Модуль 5 · Коллекции

Урок 5.1 — Массив (T[])

Фиксированная коллекция одного типа. Создание, сортировка, поиск и обход.

Массив (array) — это набор элементов одного типа, расположенных в памяти последовательно друг за другом. У каждого элемента есть индекс — номер позиции. Массивы — это фундаментальная структура данных, которая есть во всех языках программирования. В C# массив — это ссылочный тип, а не примитив.

🏗️

Создание массива

В C# есть несколько способов создать массив. Выбор зависит от того, знаешь ли ты элементы заранее или планируешь заполнить их позже.

Способ 1: new тип[размер]

Создаёт массив заданного размера. Все элементы получают значение по умолчанию (0 для чисел, false для bool, null для ссылочных типов):

C# · Создание с размером
int[] numbers = new int[5];          // { 0, 0, 0, 0, 0 }
string[] names = new string[3];      // { null, null, null }
bool[] flags = new bool[2];          // { false, false }
double[] prices = new double[4];     // { 0.0, 0.0, 0.0, 0.0 }

// После создания можно заполнить
numbers[0] = 10;
numbers[1] = 20;
numbers[2] = 30;

Способ 2: Инициализатор { ... }

Сразу перечисляешь все элементы в фигурных скобках. Размер определяется автоматически:

C# · Инициализатор
int[] numbers = { 10, 20, 30, 40, 50 };       // длина 5
string[] fruits = { "яблоко", "банан", "вишня" }; // длина 3
double[] temps = { 36.6, 37.0, 38.2, 39.5 };

// Можно и с new, но это избыточно:
int[] nums = new int[] { 1, 2, 3 };  // то же самое

Способ 3: new тип[] { ... } (явный тип)

Используется, когда нужно явно указать тип:

C# · Явный new
int[] numbers = new int[] { 1, 2, 3, 4, 5 };

// Чаще всего используется сокращённая форма без new:
int[] shortForm = { 1, 2, 3, 4, 5 };
💡 Размер массива фиксирован. После создания new int[5] ты не сможешь сделать массив длиннее или короче. Если нужно менять размер — используй List<T> (следующий урок).
🔍

Доступ к элементам и свойство Length

Доступ к элементам осуществляется через квадратные скобки с индексом. Свойство .Length возвращает количество элементов:

C# · Доступ и Length
int[] numbers = { 10, 20, 30, 40, 50 };

Console.WriteLine(numbers.Length);       // 5
Console.WriteLine(numbers[0]);           // 10 — первый
Console.WriteLine(numbers[^1]);          // 50 — последний (C# 8+)
Console.WriteLine(numbers[numbers.Length - 1]); // 50 — последний (классика)

// Изменение элемента
numbers[2] = 999;
Console.WriteLine(numbers[2]);           // 999

// Срез
int[] slice = numbers[1..4];             // { 20, 999, 40 }

Свойство Length доступно только для чтения — ты не можешь его изменить.

🔄

Перебор: for и foreach

Цикл for даёт доступ к индексу каждого элемента:

C# · for
int[] numbers = { 10, 20, 30, 40, 50 };

for (int i = 0; i < numbers.Length; i++)
{
    Console.WriteLine($"numbers[{i}] = {numbers[i]}");
}
// Вывод:
// numbers[0] = 10
// numbers[1] = 20
// numbers[2] = 30
// numbers[3] = 40
// numbers[4] = 50

Цикл foreach проще, но не даёт доступа к индексу:

C# · foreach
foreach (int num in numbers)
{
    Console.WriteLine(num);
}
// Вывод: 10 20 30 40 50 (каждый на новой строке)
📚

Статические методы Array

Класс System.Array предоставляет множество полезных статических методов для работы с массивами:

Array.Sort() — сортировка

Сортирует элементы массива по возрастанию. Для чисел — от меньшего к большему, для строк — в алфавитном порядке:

C# · Array.Sort
int[] numbers = { 5, 2, 8, 1, 9, 3 };
Array.Sort(numbers);
Console.WriteLine(string.Join(", ", numbers)); // 1, 2, 3, 5, 8, 9

string[] names = { "Вика", "Анна", "Борис" };
Array.Sort(names);
Console.WriteLine(string.Join(", ", names));   // Анна, Борис, Вика

Array.Reverse() — обратный порядок

Переворачивает массив задом наперёд:

C# · Array.Reverse
int[] numbers = { 1, 2, 3, 4, 5 };
Array.Reverse(numbers);
Console.WriteLine(string.Join(", ", numbers)); // 5, 4, 3, 2, 1

// Комбинация: сортировка по убыванию
Array.Sort(numbers);
Array.Reverse(numbers);
Console.WriteLine(string.Join(", ", numbers)); // 9, 8, 5, 3, 2, 1

Array.IndexOf() — поиск индекса

Ищет элемент в массиве и возвращает его индекс. Если элемент не найден — возвращает -1:

C# · Array.IndexOf
int[] numbers = { 10, 20, 30, 40, 50, 30 };

int index = Array.IndexOf(numbers, 30);
Console.WriteLine(index);       // 2 (первое вхождение)

index = Array.IndexOf(numbers, 99);
Console.WriteLine(index);       // -1 (не найдено)

// Поиск с указанием начального индекса:
index = Array.IndexOf(numbers, 30, 3);
Console.WriteLine(index);       // 5 (ищем с индекса 3)

Array.Find() — поиск по условию

Находит первый элемент, удовлетворяющий условию (предикату). Если ничего не найдено — возвращает default (0 для int, null для string):

C# · Array.Find
int[] numbers = { 10, 25, 30, 45, 50, 65 };

// Найти первое число больше 40
int firstBig = Array.Find(numbers, n => n > 40);
Console.WriteLine(firstBig);    // 45

string[] names = { "Анна", "Борис", "Вика", "Алексей" };
// Найти первое имя длиннее 4 символов
string longName = Array.Find(names, name => name.Length > 4);
Console.WriteLine(longName);    // "Борис"

n => n > 40 — это лямбда-выражение. Пока воспринимай это как краткую запись условия «для каждого n проверь, больше ли оно 40». Мы подробно разберём лямбды в уроках про делегаты и LINQ.

Array.FindAll() — все подходящие элементы

Возвращает новый массив со всеми элементами, удовлетворяющими условию:

C# · Array.FindAll
int[] numbers = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };

int[] evenNumbers = Array.FindAll(numbers, n => n % 2 == 0);
Console.WriteLine(string.Join(", ", evenNumbers)); // 2, 4, 6, 8, 10

int[] bigNumbers = Array.FindAll(numbers, n => n > 5);
Console.WriteLine(string.Join(", ", bigNumbers)); // 6, 7, 8, 9, 10

Array.Exists() и Array.TrueForAll()

Проверяют наличие элемента по условию и выполнение условия для всех элементов:

C# · Exists и TrueForAll
int[] numbers = { 1, 2, 3, 4, 5, 6 };

bool hasEven = Array.Exists(numbers, n => n % 2 == 0);
Console.WriteLine(hasEven);          // True (есть чётные: 2,4,6)

bool allPositive = Array.TrueForAll(numbers, n => n > 0);
Console.WriteLine(allPositive);      // True (все положительные)

bool allBig = Array.TrueForAll(numbers, n => n > 3);
Console.WriteLine(allBig);           // False (1,2,3 не больше 3)
💡 Все методы Array (Sort, Reverse, IndexOf и т.д.) изменяют исходный массив. Они не создают новый — они работают in-place.
📋

Копирование массивов

Простое присваивание не копирует массив — оно копирует ссылку. Если изменить массив через новую переменную, старый тоже изменится:

C# · Ошибка копирования
int[] original = { 1, 2, 3 };
int[] wrongCopy = original;  // ❌ Это не копия, а ещё одна ссылка!

wrongCopy[0] = 999;
Console.WriteLine(original[0]);  // 999 — original тоже изменился!

Чтобы создать настоящую копию, используй:

C# · Правильное копирование
int[] original = { 1, 2, 3 };

// Способ 1: Clone()
int[] clone = (int[])original.Clone();
clone[0] = 999;
Console.WriteLine(original[0]);  // 1 — оригинал не изменился!

// Способ 2: Array.Copy()
int[] copy = new int[original.Length];
Array.Copy(original, copy, original.Length);

// Способ 3: Срез (C# 8+)
int[] slice = original[..];

// Способ 4: Manual
int[] manual = new int[original.Length];
for (int i = 0; i < original.Length; i++)
    manual[i] = original[i];
🧊

Многомерные массивы

В C# есть два вида многомерных массивов: прямоугольные (int[,]) и зубчатые (int[][]).

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

Все строки одинаковой длины. Представь себе таблицу Excel: строки и колонки. Запись int[,] — массив из двух измерений (ряды, колонки):

C# · Прямоугольный массив
// Создание: 3 строки, 4 колонки
int[,] matrix = new int[3, 4];

// Инициализация
int[,] grid = {
    { 1, 2, 3, 4 },
    { 5, 6, 7, 8 },
    { 9, 10, 11, 12 }
};

// Доступ: [строка, колонка]
Console.WriteLine(grid[0, 0]);   // 1 — первый элемент
Console.WriteLine(grid[1, 2]);   // 7 — вторая строка, третья колонка
Console.WriteLine(grid[2, 3]);   // 12 — последний элемент

// Размеры
Console.WriteLine(grid.GetLength(0));  // 3 — количество строк
Console.WriteLine(grid.GetLength(1));  // 4 — количество колонок

// Перебор вложенными циклами
for (int row = 0; row < grid.GetLength(0); row++)
{
    for (int col = 0; col < grid.GetLength(1); col++)
    {
        Console.Write($"{grid[row, col]} ");
    }
    Console.WriteLine();
}
// Вывод:
// 1 2 3 4
// 5 6 7 8
// 9 10 11 12

Зубчатый массив int[][]

«Массив массивов». Каждая строка может быть своей длины. Объявляется как int[][] — это массив, каждый элемент которого — тоже массив:

C# · Зубчатый массив
// Создание: 3 строки, но колонки пока не заданы
int[][] jagged = new int[3][];

// Каждая строка — свой массив своей длины
jagged[0] = new int[] { 1, 2 };
jagged[1] = new int[] { 3, 4, 5, 6 };
jagged[2] = new int[] { 7, 8, 9 };

// Доступ
Console.WriteLine(jagged[0][1]);  // 2
Console.WriteLine(jagged[1][3]);  // 6
Console.WriteLine(jagged[2][0]);  // 7

// Инициализация в одной строке
int[][] data = {
    new int[] { 1, 2 },
    new int[] { 3, 4, 5, 6 },
    new int[] { 7, 8, 9 }
};

// Перебор
for (int i = 0; i < data.Length; i++)
{
    Console.Write($"Строка {i}: ");
    for (int j = 0; j < data[i].Length; j++)
    {
        Console.Write($"{data[i][j]} ");
    }
    Console.WriteLine();
}
// Вывод:
// Строка 0: 1 2
// Строка 1: 3 4 5 6
// Строка 2: 7 8 9

Когда что использовать?

int[,] (прямоугольный)
int[][] (зубчатый)
Все строки одинаковой длины
Строки разной длины
Быстрее (непрерывная память)
Медленнее (много маленьких массивов)
Для таблиц, матриц, изображений
Для разреженных данных, деревьев
🧩

Большой пример — работа с массивом студентов

Давай объединим всё, что узнали, в одну программу:

C# · Работа с массивом студентов
using System;

class Program
{
    static void Main()
    {
        // Создаём массив оценок
        int[] grades = { 85, 92, 78, 95, 88, 76, 91, 84, 73, 89 };
        
        Console.WriteLine($"Всего оценок: {grades.Length}");
        Console.WriteLine($"Первая: {grades[0]}, последняя: {grades[^1]}");
        
        // Сортировка
        Array.Sort(grades);
        Console.WriteLine($"После сортировки: {string.Join(", ", grades)}");
        
        // Поиск
        int index = Array.IndexOf(grades, 85);
        Console.WriteLine($"Индекс оценки 85: {index}");
        
        // Поиск по условию — найти первую оценку >= 90
        int highScore = Array.Find(grades, g => g >= 90);
        Console.WriteLine($"Первая высокая оценка: {highScore}");
        
        // Все отличные оценки (>= 90)
        int[] excellent = Array.FindAll(grades, g => g >= 90);
        Console.WriteLine($"Отличные оценки: {string.Join(", ", excellent)}");
        
        // Средний балл (без LINQ)
        int sum = 0;
        foreach (int grade in grades)
            sum += grade;
        double average = (double)sum / grades.Length;
        Console.WriteLine($"Средний балл: {average:F2}");
        
        // Проверка: все ли сдали (оценка >= 60)?
        bool allPassed = Array.TrueForAll(grades, g => g >= 60);
        Console.WriteLine($"Все сдали: {allPassed}");
        
        // Копия массива
        int[] backup = grades[..];
        Console.WriteLine($"Резервная копия: {string.Join(", ", backup)}");
    }
}
📊

Полезные свойства массивов

Свойство/Метод
Описание
arr.Length
Количество элементов
arr.Rank
Количество измерений (1 для одномерного, 2 для int[,])
Array.Sort(arr)
Сортировка по возрастанию
Array.Reverse(arr)
Переворот порядка элементов
Array.IndexOf(arr, val)
Поиск индекса элемента (-1 если не найден)
Array.Find(arr, pred)
Поиск первого элемента по условию
Array.FindAll(arr, pred)
Все элементы по условию
Array.Exists(arr, pred)
Есть ли хотя бы один подходящий элемент
Array.TrueForAll(arr, pred)
Все ли элементы удовлетворяют условию
📌

Что важно запомнить

1️⃣
Массив — это фиксированный набор элементов одного типа. Размер задаётся при создании и не меняется. Создать: new int[5] или { 1, 2, 3 }.
2️⃣
.Length — количество элементов. Последний индекс — Length - 1.
3️⃣
Array.Sort() — сортировка, Array.Reverse() — переворот, Array.IndexOf() — поиск индекса, Array.Find() — поиск по условию.
4️⃣
Простое присваивание не копирует массив. Используй Clone(), Array.Copy() или срез original[..].
5️⃣
Многомерные массивы: int[,] — прямоугольный (таблица), int[][] — зубчатый (массив массивов).
6️⃣
for — используй, когда нужен индекс. foreach — когда нужны только значения.

Тест: 5.1: Массив (T[])

3 вопроса

Массив и цикл

Premium