В чем основные различия между массивом (Array) и списком (List) в C#?

«В чем основные различия между массивом (Array) и списком (List) в C#?» — вопрос из категории C# Core, который задают на 25% собеседований C# Разработчик. Ниже — развёрнутый ответ с разбором ключевых моментов.

Ответ

Массив (T[]) и List<T> — фундаментальные, но принципиально разные коллекции.

Массив (Array)

  • Фиксированный размер. Размер задается при создании и не может быть изменен.
  • Непрерывная память. Элементы хранятся в смежных ячейках памяти, что обеспечивает очень быстрый доступ по индексу (O(1)).
  • Минимальные накладные расходы. Самый "легковесный" вариант коллекции.
  • Ковариантен (для ссылочных типов), что может приводить к ошибкам времени выполнения.
// Создание и инициализация массива
int[] numbersArray = new int[5] { 1, 2, 3, 4, 5 };
// numbersArray[5] = 6; // Выбросит IndexOutOfRangeException

Список (List)

  • Динамический размер. Внутри использует массив, который автоматически пересоздается с увеличенной емкостью (Capacity) при добавлении элементов сверх текущего лимита.
  • Богатый API. Предоставляет методы для удобной работы: Add, Remove, Insert, Find, Sort и многие другие.
  • Контролируемое увеличение. Удвоение Capacity при переполнении (стандартное поведение) — операция дорогая (O(n)), но амортизированная стоимость добавления остается O(1).
  • Инвариантен. Более типобезопасен, чем массив.
// Создание и работа со списком
List<int> numbersList = new List<int>() { 1, 2, 3 };
numbersList.Add(4); // Вместимость (Capacity) увеличится при необходимости
numbersList.Remove(2); // Удалит элемент со значением 2

Сравнительная таблица

Критерий Массив (T[]) List<T>
Размер Фиксированный Динамический
Производительность (доступ по индексу) Максимальная (O(1)) Высокая (O(1), но с косвенным обращением)
Производительность (вставка/удаление) Низкая (требует пересоздания) Средняя (O(n) для середины, O(1) для конца*)
Память Минимум накладных расходов Доп. память на поля класса и запас (Capacity)
Удобство API Базовые операции Богатый набор методов

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

  • Массив: Когда размер коллекции точно известен и неизменен на всем протяжении работы (например, буфер фиксированного размера, преобразование данных для низкоуровневых API).
  • List<T>: В подавляющем большинстве случаев для работы с изменяемыми коллекциями данных в бизнес-логике благодаря удобству и предсказуемой производительности.

*Примечание: Добавление в конец (Add) имеет амортизированную сложность O(1), но может вызвать дорогостоящее перераспределение внутреннего массива.