С какими типами данных чаще всего работали: списки, словари, массивы
Что проверяют
Выбираете ли вы коллекцию под задачу и понимаете ли, как она устроена внутри. Вопрос звучит просто, но следом идут уточнения про сложность операций, аллокации и поведение при росте, и там перечислением названий не отделаешься.
Как отвечать
Назовите то, чем пользуетесь ежедневно, и сразу объясняйте выбор.
- массив
T[]: фиксированная длина, элементы лежат подряд, доступ по индексу за константу. Берётся, когда размер известен заранее и важна плотность в памяти; List<T>: динамический массив поверх обычного. Добавление в конец амортизированно константное, при нехватке ёмкости выделяется новый буфер и данные копируются, вставка и удаление в середину линейные;Dictionary<TKey, TValue>: хэш-таблица, поиск по ключу в среднем за константу. Требует согласованныхGetHashCodeиEqualsу ключа;HashSet<T>для проверки принадлежности,Queue<T>иStack<T>под порядок обработки,SortedDictionary<TKey, TValue>когда нужен обход по возрастанию ключа;ConcurrentDictionary<TKey, TValue>для доступа из нескольких потоков, неизменяемые коллекции там, где состояние не должно меняться после создания.
Хорошо звучит и мысль про сигнатуры: наружу отдаём IReadOnlyList<T> или IEnumerable<T>, а конкретный тип остаётся деталью реализации.
Где спотыкаются
Считают List<T> связным списком. Это динамический массив, а связный список это LinkedList<T>, и цена операций у него принципиально другая: дешёвая вставка по известному узлу и никакого доступа по индексу.
Второе: опираются на порядок перебора словаря. Он не определён и меняется после удалений и расширения таблицы. Нужен порядок, берите отсортированную коллекцию или список пар. Рядом лежит ловушка изменяемого ключа: после правки его полей объект в словаре уже не находится.
Третье: путают отложенный запрос с готовой коллекцией. Последовательность перебирается заново при каждом обходе, а подсчёт количества у неё честно проходит все элементы, в отличие от свойства с числом элементов у списка.