Какое преимущество даст замена List на массив при удалении дубликатов
Что проверяют
Понимаете ли вы, что List<T> внутри держит обычный массив, и отличаете ли мелкую экономию на контейнере от выбора алгоритма. Честный ответ на вопрос в лоб: сама замена почти ничего не даёт. Следом почти всегда просят написать удаление дубликатов за линейное время.
Как отвечать
Начните с устройства. List<T> это обёртка над T[] плюс счётчик элементов. Элементы лежат в памяти так же подряд, индексатор списка добавляет к доступу лишь проверку по Count и один уровень косвенности. Список при нехватке места удваивает ёмкость и копирует данные, но это лечится конструктором с ёмкостью, new List<T>(n).
Главное в задаче не контейнер, а алгоритм:
- вложенный цикл: сравнить каждый элемент со всеми уже отобранными, O(n²). Одинаково медленно и на массиве, и на списке;
RemoveAtв цикле: каждое удаление сдвигает хвост, снова O(n²). У массива такой операции нет вовсе;HashSet<T>: один проход, O(n) в среднем. В одну строку:list.RemoveAll(x => !seen.Add(x)),RemoveAllуплотняет список за один проход и сохраняет порядок первых вхождений;- LINQ:
Distinct()иDistinctBy()(с .NET 6) внутри используют тот же хэш-сет, но создают новую последовательность; - отсортированные данные: два указателя по массиву или
Span<T>, O(n) и без выделения памяти. Для списка тот же приём работает черезCollectionsMarshal.AsSpan(list), а хвост потом обрезаютRemoveRange.
Массив действительно уместен, когда размер известен, код горячий и важны аллокации: например, временный буфер из ArrayPool<T>.Shared плюс Span<T>. Такие выводы подтверждают замером в BenchmarkDotNet, а не на глаз.
Где спотыкаются
Говорят, что массив быстрее, «потому что элементы лежат подряд». У списка они лежат так же, это тот же массив внутри.
Приводят вложенный цикл по массиву как оптимизированное решение. Квадратичная сложность перечёркивает любую экономию на контейнере уже на десятках тысяч элементов.
Забывают про сравнение: для своих типов HashSet и Distinct опираются на Equals и GetHashCode. Без их переопределения или своего IEqualityComparer<T> дубликаты по значению не найдутся.