Какие контейнеры (коллекции) в 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> для работы с непрерывными регионами памяти без аллокаций.

Ответ 18+ 🔞

А, ну ты глянь, какие коллекции в 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> — это как мешок с уникальными хуями. Проверить, есть ли там уже такая деталь, или добавить новую — тоже в среднем O(1). Идеально, когда надо быстро узнать, встречался ли элемент.

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

5. Stack<T> (LIFO) и Queue<T> (FIFO) — это как стопка тарелок и очередь в столовой. Первый пришёл — первый ушёл, или последний пришёл — первый ушёл. Для обхода графов или обработки задач — самое то.

6. ImmutableCollections (ImmutableList, ImmutableDictionary) — это коллекции-монашки, их после создания не изменить. Хочешь что-то добавить — получишь новую коллекцию, старую не тронешь. Потокобезопасны по умолчанию, потому что менять-то нечего, но за каждое изменение платишь созданием копии.

Совет на посошок: Если пишешь что-то высоконагруженное, где каждый чих на счету, посмотри в сторону Span<T> и Memory<T>. Это чтобы работать с памятью напрямую, без лишних аллокаций, как настоящий мужик. Но там уже надо головой думать, а не просто тыкать наугад.