Краткий ответ
по умолчанию используется List<T>
почти во всех случаях.
чуть более длинный ответ
LinkedList<T>
будет лучше, если вы будете много добавлять и удалять значения по сравнению с перечислением этих значений, а размер списка велик. Это должно быть фактором вашего выбора, только если после профилирования вы обнаружите, что использование List<T>
является проблемой.
Более длинный ответ
Предполагается, что вы определили использование одного или другого как проблему для производительности.
Если вы делаете много произвольного доступа, тогда List<T>
почти всегда будет значительно быстрее, несмотря ни на что.
Если вы много перечисляете и редко вставляете (или почти всегда вставляете около / в конце), List<T>
почти всегда будет быстрее.
Если вы постоянно вставляете / удаляете в случайных местах, но во время итерации по списку уже находитесь в соответствующем узле или рядом с ним и в нем будет как минимум несколько тысяч элементов, вы можете попробовать LinkedList<T>
Определение того, какие значения / использование приводят к повышению производительности, очень сильно зависит от вашего профиля использования. Микробенчмарки могут быть здесь очень вводящими в заблуждение, поскольку они скрывают такие аспекты поведения связанных списков, как узлы при распределении в памяти, а не прилегающие друг к другу, если они оказываются распределенными сразу в ваших тестах. Аналогично, предварительное создание List<T>
с правильным размером может иметь большое значение.
Что касается рассуждений в стиле информатики и больших обозначений O (которым действительно нужны большие N, чтобы иметь смысл в этом случае)
- работа
- стоимость
List<T>
- стоимость
LinkedList<T>
- вставить в конце
- O (1) (амортизированная стоимость, при необходимости удвоение размера)
- O (1) распределение каждый раз
- вставить при запуске
- O (N) (хотя это и происходит при быстром перемещении памяти, что несколько усложняет поведение во время работы)
- O (1)
- вставить в место x (и удалить тоже)
- O (N-x) (см. Комментарий для вставки в конце)
- O (1)
- прямое перечисление
- O (N) (хотя количество пропущенных кэшей сведено к минимуму)
- O (N) (хотя сильно зависит от локальности кэша)
- обратное перечисление
- O (N)
- O (N) (реализация
LinkedList<T>
связана дважды)
- произвольный доступ
Использование памяти является сложным, поскольку List может иметь не более Count - 1 избыточных ячеек в любой точке, но LinkedList<T>
будет потреблять LinkedListNode<T>
для каждой ячейки, что является дополнительными 3 ссылками (4/8 байт на ячейку) плюс обычный объект над головой. При нормальном использовании список, вероятно, выиграет, но опять же, это должно вызывать беспокойство только в том случае, если вы обнаружите, что потребление памяти действительно является проблемой.