В чем разница массива и списка?
Что проверяют
Понимаете ли вы, что List<T> это обёртка над массивом, и знаете ли, когда массив всё же уместен.
Как отвечать
Массив (T[]) имеет фиксированную длину, заданную при создании. Изменить её нельзя: Array.Resize на самом деле создаёт новый массив и копирует данные. Элементы лежат в памяти подряд, доступ по индексу за постоянное время.
List<T> это обёртка над массивом с автоматическим ростом. У него два разных числа: Count это сколько элементов реально есть, Capacity это размер внутреннего массива. Когда ёмкость исчерпана, создаётся массив вдвое больше и старый копируется, поэтому Add стоит амортизированно постоянное время.
Что выбирать:
List<T>по умолчанию в обычном коде: длина заранее неизвестна, нужныAdd,Remove,Insert, удобные методы;- массив когда размер известен и фиксирован, когда нужны многомерные структуры (
int[,]), при взаимодействии с неуправляемым кодом, и в горячем коде, где важна каждая аллокация.
Оптимизации, которые стоит назвать: если размер известен заранее, задавайте ёмкость в конструкторе (new List<T>(1000)), это убирает несколько перевыделений. Для чтения участка без копирования есть Span<T> и ReadOnlySpan<T>.
Отдельная деталь про типобезопасность: массивы ковариантны, то есть object[] arr = new string[10] компилируется, но попытка положить туда число падает уже во время выполнения с ArrayTypeMismatchException. У List<T> такой дыры нет, потому что он инвариантен по параметру типа.
По API наружу лучше отдавать не конкретный тип, а IReadOnlyList<T> или IEnumerable<T>, чтобы вызывающий не менял вашу внутреннюю коллекцию.
Где спотыкаются
Считают, что List<T> это связный список.
Не различают Count и Capacity.
Не знают про ковариантность массивов.
Возвращают наружу List<T> и удивляются, что вызывающий его изменил.