Какие основные виды коллекций существуют в C#?

Ответ

Коллекции в C# делятся на обобщённые (System.Collections.Generic) и необобщённые (System.Collections). В современной разработке используются почти исключительно обобщённые коллекции из-за типобезопасности и производительности.

Основные обобщённые коллекции:

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

    var numbers = new List<int> { 1, 2, 3 };
    numbers.Add(4); // Добавление в конец
    int second = numbers[1]; // Доступ по индексу
  • Dictionary<TKey, TValue> — хеш-таблица. Быстрый доступ, добавление и удаление по ключу (в среднем O(1)). Ключи должны быть уникальными.

    var capitals = new Dictionary<string, string>
    {
        ["France"] = "Paris",
        ["Japan"] = "Tokyo"
    };
    string city = capitals["Japan"]; // Получение по ключу
  • HashSet<T> — коллекция уникальных элементов на основе хеш-таблицы. Оптимизирована для операций проверки принадлежности (Contains), объединения (UnionWith), пересечения (IntersectWith).

    var uniqueNumbers = new HashSet<int> { 1, 2, 2, 3 }; // Результат: {1, 2, 3}
    bool added = uniqueNumbers.Add(2); // Вернёт false, так как 2 уже есть
  • Queue<T> — коллекция по принципу FIFO (First-In-First-Out).

    var queue = new Queue<string>();
    queue.Enqueue("First");
    queue.Enqueue("Second");
    string firstItem = queue.Dequeue(); // Вернёт "First"
  • Stack<T> — коллекция по принципу LIFO (Last-In-First-Out).

    var stack = new Stack<string>();
    stack.Push("First");
    stack.Push("Second");
    string topItem = stack.Pop(); // Вернёт "Second"
  • LinkedList<T> — двусвязный список. Очень быстрая вставка и удаление в любом месте (O(1)), если есть ссылка на узел, но медленный доступ по индексу (O(n)).

Выбор коллекции:

  • Используйте List<T> по умолчанию для простых списков.
  • Используйте Dictionary<TKey, TValue> для быстрого поиска по ключу.
  • Используйте HashSet<T>, когда важна уникальность и быстрая проверка наличия элемента.
  • Необобщённые коллекции (ArrayList, Hashtable) считаются устаревшими и их следует избегать из-за проблем с производительностью (упаковка/распаковка) и типобезопасностью.

Ответ 18+ 🔞

Давай разберём эти ваши коллекции, а то некоторые до сих пор ArrayList юзают, как будто на дворе не 2024-й, а 2002-й год, блядь.

Смотри, в C# есть два лагеря: обобщённые (System.Collections.Generic) и необобщённые (System.Collections). Так вот, если ты сейчас пишешь новый код и лезешь в System.Collections — ты или старый пердун, который не хочет учиться, или просто еблан. Необобщённые коллекции — это прошлый век, они медленные из-за постоянной упаковки-распаковки и опасные, потому что там любой объект можно запихнуть, а потом получить InvalidCastException прямо в ебало. Забудь про них, как про плохой сон.

Теперь про нормальные, обобщённые коллекции — это наш рабочий инструмент.

Основные пацаны на районе:

  • List<T> — это типа динамический массив, самый популярный чувак. Доступ по индексу — мгновенный, O(1). Но если тебе взбредёт вставить или удалить что-то в середину — будет больно, O(n), потому что всё после этой точки придётся сдвигать.

    var numbers = new List<int> { 1, 2, 3 };
    numbers.Add(4); // Добавили в конец — быстро
    int second = numbers[1]; // Достали по индексу — моментально
  • Dictionary<TKey, TValue> — хеш-таблица, король поиска по ключу. В среднем все операции (добавить, найти, удалить) — O(1), что просто охуенно. Ключи должны быть уникальными, иначе он тебе мозги выест.

    var capitals = new Dictionary<string, string>
    {
        ["France"] = "Paris",
        ["Japan"] = "Tokyo"
    };
    string city = capitals["Japan"]; // Бах — и Токио у тебя в кармане
  • HashSet<T> — это как Dictionary, но только для ключей. Хранит только уникальные элементы. Создан для быстрых операций: проверить, есть ли элемент (Contains), объединить два множества, найти общие элементы. Если попытаться добавить дубликат — он вежливо посылает тебя нахуй (вернёт false).

    var uniqueNumbers = new HashSet<int> { 1, 2, 2, 3 }; // В итоге будет {1, 2, 3}
    bool added = uniqueNumbers.Add(2); // Вернёт false, потому что двойка уже есть, мудила
  • Queue<T> — очередь, принцип FIFO (первым пришёл — первым ушёл). Как в магазине, только без бабулек с тележками.

    var queue = new Queue<string>();
    queue.Enqueue("First");
    queue.Enqueue("Second");
    string firstItem = queue.Dequeue(); // Вытащит "First"
  • Stack<T> — стек, принцип LIFO (последним пришёл — первым ушёл). Как стопка тарелок: какую положил сверху — такую и снимаешь.

    var stack = new Stack<string>();
    stack.Push("First");
    stack.Push("Second");
    string topItem = stack.Pop(); // Вытащит "Second"
  • LinkedList<T> — двусвязный список. Экзотический зверь. Если у тебя есть прямая ссылка на узел, то вставить или удалить рядом с ним — мгновенно, O(1). Но чтобы до этого узла добраться по индексу — придётся идти пешком через весь список, O(n). Используется редко, для специфичных оптимизаций.

Краткий гид по выбору, чтобы не выглядеть идиотом:

  • Просто нужен список? List<T>, без вариантов.
  • Нужно быстро что-то найти по ключу (ID, имя)? Dictionary<TKey, TValue>, идеально.
  • Важна уникальность и нужно быстро проверять, есть ли элемент? HashSet<T>, в студию.
  • Очередь задач или сообщений? Queue<T>.
  • Отмена операций или история? Stack<T>.

И ещё раз, для тех, кто в танке: забей хуй на ArrayList, Hashtable и всю эту необобщённую хуйню. Это legacy, от которого одни проблемы.