> С какими типами данных чаще всего работали: списки, словари, массивы (C#)
Уровень: senior · Роль: backend · Язык: C# · Категория: Технические вопросы
Компании: ATLASстартап
Стек: C# /.NET
> Пример ответа
Короткий ответ
В C# чаще всего работал со списками (List<T>) и словарями (Dictionary<TKey, TValue>). Массивы (T[]) использовал реже - в основном для фиксированных буферов, низкоуровневых операций или hot path, где важна производительность без аллокаций. Словари - основной инструмент для lookup-сценариев, списки - для динамических коллекций с последовательным доступом. Также активно использовал HashSet<T> для проверки уникальности и IReadOnlyCollection<T> для контрактов API.
Подробное объяснение
Выбор типа данных в C# определяется не только семантикой, но и особенностями runtime:
List<T>- динамический массив с амортизированной O(1) вставкой в конец. Индексация O(1), вставка/удаление в середине - O(n). Использую как основную коллекцию по умолчанию.Dictionary<TKey, TValue>- hash table, O(1) в среднем для lookup/insert/delete. Критично правильно выбирать comparer (default vs ordinal для строк). При большом количестве коллизий деградирует до O(n).T[]- фиксированный размер, минимальный overhead, cache-friendly. Использую дляSpan<T>,stackalloc, буферов, сериализации.HashSet<T>- по сути dictionary без значений, полезен для deduplication и set operations.LinkedList<T>- практически не использую, так как в большинстве случаевList<T>с индексами эффективнее из-за cache locality.ConcurrentDictionary<TKey, TValue>- для многопоточных сценариев, но с осторожностью: lock-free внутри, но операции не атомарны без дополнительной синхронизации.
Важный trade-off: List<T> аллоцирует массив с запасом (capacity), что может приводить к лишней памяти. Для больших коллекций с известным размером лучше сразу задавать capacity или использовать массивы.
На практике
В реальных backend-задачах на C#:
- Для запросов к БД через EF Core -
IQueryable<T>→List<T>после материализации. - Для кэшей -
ConcurrentDictionary<TKey, TValue>илиMemoryCache. - Для обработки пайплайнов -
IEnumerable<T>с ленивой оценкой, но осторожно: множественные итерации поIEnumerableмогут привести к повторному выполнению запроса. - Для high-performance сценариев -
ArrayPool<T>,Span<T>,Memory<T>вместо массивов и списков. - Для API-контрактов -
IReadOnlyList<T>иIReadOnlyDictionary<TKey, TValue>чтобы не раскрывать изменяемость.
Типичный паттерн: получить данные из БД → List<T>, построить Dictionary<TKey, TValue> для lookup, вернуть IReadOnlyCollection<T> наружу.
Пример кода
CSHARP// Типичный сценарий: маппинг и lookuppublic class OrderService{private readonly Dictionary<Guid, Order> _ordersById = new();private readonly List<Order> _recentOrders = new();private readonly HashSet<Guid> _processedOrderIds = new();public void ProcessOrders(IEnumerable<Order> orders){foreach (var order in orders){if (!_processedOrderIds.Add(order.Id))continue; // уже обработан_ordersById[order.Id] = order;_recentOrders.Add(order);}}public Order? FindOrder(Guid id) =>_ordersById.GetValueOrDefault(id);// Для hot path - массив и Spanpublic double CalculateAverage(int[] values){Span<int> span = values;long sum = 0;foreach (var v in span) sum += v;return (double)sum / values.Length;}}
Как отвечать на собеседовании
Начните с прямого ответа: List<T> и Dictionary<TKey, TValue> - основные. Затем объясните критерии выбора: сложность операций, аллокации, cache locality, потокобезопасность. Приведите конкретные примеры из практики: кэширование, маппинг DTO, обработка пайплайнов. Упомяните, что массивы используете для fixed-size буферов и Span<T>. Покажите понимание внутреннего устройства: capacity, resize, hash collisions. Если спросят про LinkedList<T> - объясните, почему он редко нужен. Завершите упоминанием современных API: ImmutableArray<T>, FrozenDictionary<TKey, TValue> в .NET 8+.
Что проверяет интервьюер
- Понимание сложности операций и trade-off между коллекциями.
- Умение выбирать структуру данных под задачу, а не по привычке.
- Знание внутреннего устройства: capacity, resize, hash collisions, comparer.
- Осведомленность о современных API:
Span<T>,Memory<T>,FrozenDictionary. - Понимание потокобезопасности и когда нужен
ConcurrentDictionary. - Умение объяснить, почему
List<T>часто лучшеLinkedList<T>в .NET.
Типичные ошибки
- Говорить "массивы" как основной тип, не упоминая
List<T>- это сразу выдаёт слабое знание C#. - Не различать
IEnumerable<T>иList<T>: множественная итерация поIEnumerable- частая ошибка. - Использовать
Dictionary<TKey, TValue>для маленьких коллекций (до ~10 элементов) - линейный поиск по массиву быстрее. - Забывать про
HashSet<T>для проверки уникальности - вместо этого используютList.Contains(), что даёт O(n). - Не упоминать capacity:
new List<T>(n)иnew Dictionary<TKey, TValue>(n)экономят реаллокации. - Путать
ConcurrentDictionaryс потокобезопасностью отдельных операций - составные операции требуют внешней синхронизации.
> Похожие задачи по C#
> Похожие задачи по backend
Будет ли метод работать корректно при бесконечном input?
Какие алгоритмы балансировки существуют
Какую архитектуру и технологии использовать для реализации клиента чата с REST API?
Как описать схему базы данных для модели библиотеки с сущностями автор, книга, читатель и связями между ними
> ГОТОВЫ К СЛЕДУЮЩЕМУ СОБЕСЕДОВАНИЮ?
Запустите тренировочную сессию с ИИ и получите детальную обратную связь, чтобы увереннее проходить реальные интервью