Что такое List в C#?

Ответ

List — это универсальная коллекция, предоставляющая динамический массив с возможностью автоматического изменения размера. Это одна из самых часто используемых структур данных в C#.

Основные особенности:

  • Динамический размер: В отличие от массива (T[]), List<T> автоматически увеличивает свою внутреннюю емкость при добавлении элементов.
  • Доступ по индексу: Поддерживает быстрый доступ к элементам по индексу за O(1): myList[5].
  • Богатый API: Содержит множество удобных методов для добавления (Add, AddRange), удаления (Remove, RemoveAt), поиска (Find, Contains) и манипуляций (Sort, Reverse).

Пример использования:

// Создание списка с начальной емкостью (оптимизация)
List<string> cities = new List<string>(capacity: 10);

// Добавление элементов
cities.Add("Москва");
cities.AddRange(new[] { "Санкт-Петербург", "Новосибирск" });

// Вставка в середину (дорогая операция O(n))
cities.Insert(1, "Екатеринбург");

// Удаление
cities.Remove("Новосибирск"); // Удаляет первое вхождение
cities.RemoveAt(0); // Удаляет элемент по индексу

// Поиск
bool hasMoscow = cities.Contains("Москва");
string firstCity = cities.Find(c => c.StartsWith("С")); // Санкт-Петербург

// Итерация
foreach (var city in cities) { /* ... */ }

Сложность операций и рекомендации:

  • Доступ по индексу: O(1).
  • Добавление в конец (Add): В среднем O(1). Если внутренний массив заполнен, происходит выделение нового массива (обычно в 2 раза больше) и копирование элементов (O(n)).
  • Вставка/удаление в середину: O(n), так как требует сдвига всех последующих элементов.
  • Когда использовать: Для хранения коллекций, где часты операции доступа по индексу и добавления в конец. Если нужны частые вставки/удаления в начале или середине, рассмотрите LinkedList<T>.

Ответ 18+ 🔞

А, List! Ну это ж классика, как водка с огурцом. Представь себе обычный массив, но который не орёт "места нет, пошёл нахуй", когда ты пытаешься впихнуть в него очередной элемент, а тихонько расширяется, как твои джинсы после новогоднего салата.

Короче, что это за зверь: Это такая умная обёртка над массивом, которая делает вид, что она бесконечная. Внутри всё тот же простой массив, но когда он заполняется, List не паникует, а спокойно создаёт новый массив, побольше (обычно в два раза), и перетаскивает туда все старые элементы. Работает, как трактор — медленно, но верно.

Чем хорош:

  • Достать что-то по номеру — мгновенно. myList[5] — и ты уже получил свой элемент, быстрее, чем сосед успел крикнуть "заткнись, там футбол!".
  • Добавить в конец — обычно тоже быстро. Пока есть место в его внутреннем массиве — всё ок. А когда место кончится... ну, будет небольшая заминка на переезд.
  • Методов дохуя. Хочешь добавить, удалить, найти, перевернуть, отсортировать — всё есть. Не нужно, как в старые времена, руками писать циклы на коленке.

Пример, чтобы стало совсем ясно:

// Создаём список. Можно сразу сказать, сколько примерно элементов будет — для приличия.
List<string> пивные = new List<string>(10);

// Наливаем.
пивные.Add("Жигулёвское");
пивные.AddRange(new[] { "Балтика 9", "Очаково" });

// Впихнуть что-то в середину — можно, но дорого. Ему придётся сдвигать всё, что после.
пивные.Insert(1, "Сибирская корона");

// Выпили что-то.
пивные.Remove("Очаково"); // Выкинул первое попавшееся
пивные.RemoveAt(0); // Выкинул самое первое, по индексу

// Поиск.
bool естьЛиБалтика = пивные.Contains("Балтика 9"); // true, надеюсь
string крепкоеПиво = пивные.Find(p => p.Contains("9")); // Найдёт "Балтика 9"

// Перебор, как по магазину.
foreach (var бутылка in пивные)
{
    Console.WriteLine($"Открывай {бутылка}!");
}

Где собака зарыта (производительность):

  • Взять элемент по индексу ([5]) — O(1). Быстро, как удар током.
  • Добавить в конец (Add) — В среднем O(1). Но если он упёрся в потолок внутреннего массива, то будет операция "расширяемся" — O(n), и все на это время зависнут.
  • Вставить или удалить где-то в середине (Insert, Remove) — O(n). Это пиздец как медленно, потому что ему надо будет подвинуть кучу элементов, как шкаф с посудой. Если делаешь это часто — ты конченый мазохист.

Так когда его использовать-то? Используй его, когда тебе чаще всего нужно просто добавлять в конец и доставать по номеру. Это как твоя домашняя коллекция видеокассет — ты их ставишь на полку (в конец) и иногда берёшь посмотреть (по индексу).

А если тебе нужно постоянно вставлять и удалять что-то в начале или середине — это пиздец, братан, выбери что-то другое, например, LinkedList<T>. С ним вставка в середину быстрая, но зато достать что-то по номеру — это уже отдельный квест с похмелья.

Короче, List — это рабочий лошадь. Не самая быстрая для всего подряд, но предсказуемая и удобная в 95% случаев. Главное — не пытайся впендюривать элементы в его середину в цикле, а то производительность накроется медным тазом.