Глиф Учебники

Урок 6. Массивы и списки List<T>

Курс «C#» · урок 6 из 10 · 3 минуты чтения

Отметки за четверть, список покупок, координаты точек — часто нужно хранить не одно значение, а много. Для этого есть массивы и списки.

Массив

Массив — набор значений одного типа, у каждого свой номер — индекс. Индексы начинаются с нуля: у массива из пяти элементов они от 0 до 4.

C#
int[] marks = { 7, 9, 6, 10, 8 };
Console.WriteLine(marks.Length);
Console.WriteLine(marks[0]);
Console.WriteLine(marks[marks.Length - 1]);
marks[2] = 5;
Console.WriteLine(string.Join(", ", marks));
Вывод
5
7
8
7, 9, 5, 10, 8

Размер массива задаётся при создании и потом не меняется. Пустой массив нужного размера создают через new: new int[5] — пять нулей, new string[3] — три пустые ссылки null, new bool[2] — два false.

Обращение к индексу за пределами массива — marks[5] у массива из пяти элементов — останавливает программу ошибкой IndexOutOfRangeException. Что с ней делать — в справке Ошибка IndexOutOfRangeException.

marks [^1] [0] [1] [2] [3] [4] 7 9 6 10 8 [5] нет marks.Length — 5 элементов IndexOutOfRangeException
У массива marks из пяти элементов индексы от 0 до 4, к последнему можно обратиться и через ^1, а индекса 5 нет: обращение к нему даёт IndexOutOfRangeException.

Обход массива

Пройти по элементам можно циклом for по индексам или foreach по значениям:

C#
int[] marks = { 7, 9, 6, 10, 8 };
int sum = 0;
int max = marks[0];
foreach (int m in marks)
{
    sum += m;
    if (m > max)
    {
        max = m;
    }
}
double average = (double)sum / marks.Length;
Console.WriteLine($"Сумма: {sum}, максимум: {max}");
Console.WriteLine($"Среднее: {average:F1}");
Вывод
Сумма: 40, максимум: 10
Среднее: 8,0

foreach удобнее, когда нужны только значения. for нужен, если важен номер элемента или элементы нужно менять: marks[i] = 0;.

Сортировка и печать

C#
int[] a = { 5, 3, 8, 1 };
Array.Sort(a);
Console.WriteLine(string.Join(" ", a));
Array.Reverse(a);
Console.WriteLine(string.Join(" ", a));
Console.WriteLine(a);
Вывод
1 3 5 8
8 5 3 1
System.Int32[]

Array.Sort сортирует по возрастанию, Array.Reverse переворачивает. Обратите внимание на последнюю строку: Console.WriteLine(a) печатает не элементы, а название типа массива. Элементы выводят через string.Join или циклом.

Массив с клавиатуры

Числа обычно вводят в одной строке через пробел. Строку делят на части, а каждую часть переводят в число:

C#
string[] parts = (Console.ReadLine() ?? "").Split(' ', StringSplitOptions.RemoveEmptyEntries);
int[] numbers = new int[parts.Length];
for (int i = 0; i < parts.Length; i++)
{
    numbers[i] = int.Parse(parts[i]);
}
Console.WriteLine($"Чисел: {numbers.Length}, сумма: {numbers.Sum()}");
Ввод
4 8 15 16 23 42
Вывод
Чисел: 6, сумма: 108

numbers.Sum() — готовая сумма из библиотеки LINQ (о ней — справка LINQ коротко). Цикл перевода можно записать одной строкой: int[] numbers = Array.ConvertAll(parts, int.Parse);.

Список List<T>

У массива постоянный размер. Если элементы нужно добавлять и удалять, берут список List<T>, где вместо T пишется тип элементов: List<int>, List<string>.

C#
List<string> names = new List<string>();
names.Add("Анна");
names.Add("Борис");
names.Add("Вера");
Console.WriteLine(names.Count);
names.Insert(1, "Глеб");
Console.WriteLine(string.Join(", ", names));
names.Remove("Борис");
names.RemoveAt(0);
Console.WriteLine(string.Join(", ", names));
Console.WriteLine(names.Contains("Вера"));
Console.WriteLine(names[0]);
Вывод
3
Анна, Глеб, Борис, Вера
Глеб, Вера
True
Глеб
names.Add("Вера"); "Анна" 0 "Борис" 1 "Вера" 2 names.Insert(1, "Глеб"); "Анна" 0 "Глеб" 1 "Борис" 2 "Вера" 3 names.Remove("Борис"); "Анна" 0 "Глеб" 1 "Вера" 2 "Борис" удалён names.RemoveAt(0); "Глеб" 0 "Вера" 1 "Анна" удалён
Add кладёт элемент в конец списка, Insert вставляет его на место 1 и сдвигает следующие, а после Remove и RemoveAt индексы оставшихся уменьшаются.
Что сделатьКак
добавить в конецlist.Add(x)
вставить на место ilist.Insert(i, x)
удалить значение (первое найденное)list.Remove(x)
удалить по индексуlist.RemoveAt(i)
сколько элементовlist.Count (у массива — Length)
есть ли значениеlist.Contains(x)
где значениеlist.IndexOf(x) — индекс или −1
отсортироватьlist.Sort()
очиститьlist.Clear()

Список можно сразу заполнить: var primes = new List<int> { 2, 3, 5, 7 };.

Пример: числа до нуля

Сколько чисел введут, заранее неизвестно — тут список удобнее массива:

C#
List<int> numbers = new List<int>();
while (true)
{
    int x = int.Parse(Console.ReadLine() ?? "");
    if (x == 0)
    {
        break;
    }
    numbers.Add(x);
}
numbers.Sort();
Console.WriteLine($"Введено: {numbers.Count}");
Console.WriteLine($"По возрастанию: {string.Join(" ", numbers)}");
Ввод
5
2
9
1
0
Вывод
Введено: 4
По возрастанию: 1 2 5 9

Массив — это ссылка

Переменная массива или списка хранит не сами элементы, а ссылку на них. Присваивание копирует ссылку, и две переменные смотрят на один и тот же массив:

C#
int[] a = { 1, 2, 3 };
int[] b = a;
b[0] = 100;
Console.WriteLine(a[0]);
Вывод
100

Настоящую копию делает (int[])a.Clone() или a.ToArray(), копию списка — new List<int>(list).

int[] b = a; b[0] = 100; a b 100 2 3 один массив — a[0] тоже 100 b — копия: a.ToArray() b[0] = 100; a b 1 2 3 100 2 3 два массива — a[0] остался 1
После присваивания b = a обе переменные ссылаются на один массив, и запись через b видна через a, а копия из a.ToArray() — отдельный массив.

Двумерные массивы

Таблицу чисел хранит двумерный массив: int[,] grid = new int[3, 4]; — 3 строки по 4 столбца. Элемент — grid[1, 2], число строк — grid.GetLength(0), столбцов — grid.GetLength(1). Обходят его двумя вложенными циклами for.

Коротко

Проверьте себя

1. Какой индекс у последнего элемента массива a из 10 элементов?
2. Что выведет Console.WriteLine(new int[] { 1, 2, 3 });?
3. Чем List<int> удобнее массива int[]?
4. Что выведет программа?
C#
List<int> list = new List<int> { 4, 7, 4, 1 };
list.Remove(4);
Console.WriteLine(string.Join(" ", list));