Какие контейнеры (коллекции) в C# ты использовал?

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

Ответ

Выбор коллекции в C# критически влияет на производительность. Вот основные типы и сценарии их использования:

1. List<T> — универсальная коллекция для последовательного хранения. Быстрый доступ по индексу (O(1)), но вставка/удаление в середину — O(n).

var list = new List<int> { 1, 2, 3 };
list.Add(4); // O(1) амортизированно
var item = list[2]; // O(1)
list.Insert(1, 99); // O(n) - сдвиг элементов

2. Dictionary<TKey, TValue> — хеш-таблица для пар ключ-значение. Поиск, вставка, удаление — в среднем O(1). Важно: переопределять GetHashCode() и Equals() для ключей-ссылочных типов.

var dict = new Dictionary<string, int>(StringComparer.OrdinalIgnoreCase);
dict.TryAdd("Key", 42); // Лучше, чем dict["Key"] = 42, если ключ может существовать

3. HashSet<T> — коллекция уникальных элементов на основе хеш-таблицы. Операции Contains, Add, RemoveO(1). Идеален для проверок принадлежности.

4. LinkedList<T> — двусвязный список. Вставка/удаление в известной позиции — O(1), но доступ по индексу — O(n). Полезен для реализации LRU-кэша или когда важны частые вставки в середину.

5. Stack<T> (LIFO) и Queue<T> (FIFO) — специализированные коллекции для алгоритмов (обход графа, BFS/DFS) и обработки задач.

6. ImmutableCollections (ImmutableList, ImmutableDictionary) — из пространства имен System.Collections.Immutable. Потокобезопасны по умолчанию (так как неизменяемы), но каждая операция создает новую коллекцию.

Рекомендация: Для высоконагруженных сценариев рассматривайте Span<T> и Memory<T> для работы с непрерывными регионами памяти без аллокаций.