Когда лучше использовать List против LinkedList ?
c#
.net
vb.net
data-structures
linked-list
Джонатан Аллен
источник
источник
Ответы:
редактировать
Оригинальный ответ ...
Я нашел интересные результаты:
Связанный список (3,9 секунды)
Список (2,4 секунды)
Даже если вы только получаете доступ к данным, это существенно медленнее! Я говорю, никогда не используйте связанный список.
Вот еще одно сравнение, выполняющее много вставок (мы планируем вставить элемент в середине списка)
Связанный список (51 секунда)
Список (7,26 секунд)
Связанный список, имеющий ссылку на место, куда вставить (0,04 секунды)
Таким образом, только если вы планируете вставить несколько элементов, и у вас также есть ссылка на то, куда вы планируете вставить элемент, используйте связанный список. Просто потому, что вам нужно вставить много элементов, это не делает это быстрее, потому что поиск места, куда вы хотели бы вставить, занимает время.
источник
list.AddLast(a);
в последних двух примерах LinkedList? Я делаю это один раз перед циклом, какlist.AddLast(new Temp(1,1,1,1));
в следующем за последним LinkedList, но мне кажется, что вы добавляете вдвое больше объектов Temp в самих циклах. (И когда я дважды проверяю себя в тестовом приложении , конечно же, вдвое больше, чем в LinkedList.)I say never use a linkedList.
ошибочен, как показывает ваш последующий пост. Вы можете отредактировать его. 2) Какое у вас время? Инстанцирование, сложение и перечисление всего за один шаг? Главным образом, создание экземпляров и перечисление - это не то, о чем беспокоятся люди, это одноразовые шаги. В частности, сроки вставки и дополнения даст лучшую идею. 3) Самое главное, вы добавляете больше, чем требуется, в связанный список. Это неправильное сравнение. Распространяет неправильное представление о связном списке.В большинстве случаев
List<T>
это более полезно.LinkedList<T>
будет стоить меньше при добавлении / удалении элементов в середине списка, тогда какList<T>
добавление / удаление в конце списка будет дешевым .LinkedList<T>
только в наиболее эффективном случае, если вы обращаетесь к последовательным данным (в прямом или обратном направлении) - произвольный доступ является относительно дорогим, поскольку он должен обходить цепочку каждый раз (следовательно, поэтому у него нет индексатора). Тем не менее, поскольку aList<T>
по сути является просто массивом (с оберткой), произвольный доступ - это нормально.List<T>
также предлагает много методов поддержки -Find
,ToArray
и т.д.; тем не менее, они также доступны дляLinkedList<T>
.NET 3.5 / C # 3.0 с помощью методов расширения, так что это менее важный фактор.источник
List<T>
иT[]
потерпит неудачу за то, что он слишком длинный (все одна плита),LinkedList<T>
будет плачем за то, что он слишком гранулированный (плита за элемент).Представление связанного списка как списка может быть немного обманчивым. Это больше похоже на цепь. На самом деле в .NET
LinkedList<T>
даже не реализуютIList<T>
. В связанном списке нет реальной концепции индекса, хотя может показаться, что она есть. Конечно, ни один из методов, предоставленных в классе, не принимает индексы.Связанные списки могут быть односвязными или двусвязными. Это относится к тому, имеет ли каждый элемент в цепочке ссылку только на следующий (односвязный) или на оба предыдущих / следующих элемента (двусвязный).
LinkedList<T>
вдвойне связан.Внутренне,
List<T>
поддерживается массивом. Это обеспечивает очень компактное представление в памяти. И наоборот,LinkedList<T>
включает в себя дополнительную память для хранения двунаправленных связей между последовательными элементами. Таким образом, объем занимаемой памятиLinkedList<T>
обычно будет больше, чем дляList<T>
(с оговоркой, котораяList<T>
может иметь неиспользуемые элементы внутреннего массива для повышения производительности во время операций добавления).Они также имеют разные характеристики производительности:
Append
LinkedList<T>.AddLast(item)
постоянное времяList<T>.Add(item)
амортизированное постоянное время, линейный наихудший случайPrepend
LinkedList<T>.AddFirst(item)
постоянное времяList<T>.Insert(0, item)
линейное времявставка
LinkedList<T>.AddBefore(node, item)
постоянное времяLinkedList<T>.AddAfter(node, item)
постоянное времяList<T>.Insert(index, item)
линейное времяУдаление
LinkedList<T>.Remove(item)
линейное времяLinkedList<T>.Remove(node)
постоянное времяList<T>.Remove(item)
линейное времяList<T>.RemoveAt(index)
линейное времяподсчитывать
LinkedList<T>.Count
постоянное времяList<T>.Count
постоянное времяСодержит
LinkedList<T>.Contains(item)
линейное времяList<T>.Contains(item)
линейное времяОчистить
LinkedList<T>.Clear()
линейное времяList<T>.Clear()
линейное времяКак видите, они в основном эквивалентны. На практике API
LinkedList<T>
более громоздок в использовании, и детали его внутренних потребностей выливаются в ваш код.Однако, если вам нужно сделать много вставок / удалений из списка, он предлагает постоянное время.
List<T>
предлагает линейное время, так как дополнительные элементы в списке должны быть перемешаны после вставки / удаления.источник
Связанные списки обеспечивают очень быструю вставку или удаление члена списка. Каждый член в связанном списке содержит указатель на следующий член в списке, поэтому для вставки члена в позицию i:
Недостатком связанного списка является то, что произвольный доступ невозможен. Доступ к члену требует обхода списка, пока не будет найден нужный член.
источник
Мой предыдущий ответ был недостаточно точным. Как верно было ужасно: D Но теперь я могу опубликовать гораздо более полезный и правильный ответ.
Я сделал несколько дополнительных тестов. Вы можете найти его источник по следующей ссылке и заново проверить его в своей среде: https://github.com/ukushu/DataStructuresTestsAndOther.git
Краткие результаты:
Массив нужно использовать:
Список нужно использовать:
LinkedList нужно использовать:
Больше деталей:
Интересно знать:
LinkedList<T>
внутренне это не список в .NET. Это даже не реализоватьIList<T>
. И именно поэтому отсутствуют индексы и методы, связанные с индексами.LinkedList<T>
коллекция на основе указателя узла. В .NET это в двусвязной реализации. Это означает, что предыдущие / следующие элементы имеют ссылку на текущий элемент. И данные фрагментированы - разные объекты списка могут быть расположены в разных местах оперативной памяти. Также будет использовано больше памяти,LinkedList<T>
чем дляList<T>
или для массива.List<T>
в .Net является альтернативой JavaArrayList<T>
. Это означает, что это оболочка массива. Таким образом, он выделяется в памяти как один непрерывный блок данных. Если выделенный размер данных превышает 85000 байт, он будет перемещен в кучу больших объектов. В зависимости от размера это может привести к фрагментации кучи (легкая форма утечки памяти). Но в то же время, если размер <85000 байт - это обеспечивает очень компактное и быстрое представление в памяти.Один непрерывный блок предпочтителен для производительности произвольного доступа и потребления памяти, но для коллекций, которым необходимо регулярно менять размер, структуру, такую как массив, обычно необходимо копировать в новое место, тогда как связанный список должен управлять только памятью для вновь вставленного / удаленные узлы.
источник
Разница между List и LinkedList заключается в их базовой реализации. Список - это массив на основе массива (ArrayList). LinkedList - это основанная на указателе узла коллекция (LinkedListNode). Что касается использования уровня API, то оба они в значительной степени одинаковы, поскольку оба реализуют один и тот же набор интерфейсов, таких как ICollection, IEnumerable и т. Д.
Ключевое различие возникает, когда производительность имеет значение. Например, если вы реализуете список со сложной операцией «INSERT», LinkedList превосходит List. Поскольку LinkedList может сделать это за O (1) раз, но List может потребоваться расширить размер базового массива. Для получения дополнительной информации вы можете прочитать об алгоритмическом различии между LinkedList и структурами данных массива. http://en.wikipedia.org/wiki/Linked_list и Array
Надеюсь, это поможет,
источник
Add
всегда в конце существующего массива.List
достаточно «хорош», даже если не O (1). Серьезная проблема возникает, если вам нужно многоAdd
s, которых нет в конце. Марк отмечает, что необходимость перемещать существующие данные каждый раз, когда вы вставляете (а не только когда требуется изменение размера), является более существенной потерей производительностиList
.Основное преимущество связанных списков над массивами состоит в том, что ссылки дают нам возможность эффективно переупорядочивать элементы. Седжвик, р. 91
источник
Обычное обстоятельство использования LinkedList выглядит следующим образом:
Предположим, вы хотите удалить много определенных строк из списка строк большого размера, скажем, 100 000. Строки для удаления можно найти в HashSet dic, и список строк, как полагают, содержит от 30000 до 60000 таких строк для удаления.
Тогда какой тип списка лучше всего подходит для хранения 100 000 строк? Ответ LinkedList. Если они хранятся в ArrayList, то итерация по нему и удаление совпадающих строк может занять до миллиардов операций, в то время как с помощью итератора и метода remove () требуется всего около 100 000 операций.
источник
RemoveAll
для удаления элементы из,List
не перемещая много элементов, или использоватьWhere
из LINQ для создания второго списка. Однако использованиеLinkedList
здесь приводит к значительному увеличению объема памяти по сравнению с другими типами коллекций, а потеря локальности памяти означает, что итерация будет заметно медленнее, что делает его немного хуже, чем aList
.RemoveAll
эквивалент в Java.RemoveAll
недоступно дляList
, вы можете выполнить алгоритм «уплотнения», который будет выглядеть как цикл Тома, но с двумя индексами и необходимостью перемещать элементы, которые будут храниться по одному во внутреннем массиве списка. Эффективность равна O (n), так же, как алгоритм Тома дляLinkedList
. В обеих версиях время для вычисления ключа HashSet для строк доминирует. Это не хороший пример того, когда использоватьLinkedList
.Когда вам нужен встроенный индексированный доступ, сортировка (и после этого двоичного поиска) и метод «ToArray ()», вы должны использовать List.
источник
По сути,
List<>
в .NET это обертка над массивом . АLinkedList<>
является связанным списком . Таким образом, вопрос сводится к тому, какова разница между массивом и связанным списком, и когда следует использовать массив вместо связанного списка. Вероятно, два наиболее важных фактора в вашем решении, которые следует использовать, сводятся к следующему:источник
Это адаптировано из принятого ответа Тоно Нама, исправляющего несколько неправильных измерений в нем.
Тест:
И код:
Вы можете увидеть результаты в соответствии с теоретическими характеристиками, которые другие документировали здесь. Совершенно ясно -
LinkedList<T>
выигрывает в случае вставок. Я не проверял удаление из середины списка, но результат должен быть таким же. Конечно,List<T>
есть и другие области, где он работает лучше, чем O (1) произвольный доступ.источник
Используйте
LinkedList<>
когдаToken Stream
.Для всего остального лучше использовать
List<>
.источник
LinkedListNode<T>
объекты в вашем коде. Если вы можете сделать это, то это гораздо лучше, чем использоватьList<T>
, особенно для очень длинных списков, где часто вставляются / удаляются записи.node.Value
всякий раз, когда хотите оригинальный элемент). Таким образом, вы переписываете алгоритм для работы с узлами, а не с необработанными значениями.Я согласен с большей частью высказанного выше. И я также согласен, что список выглядит как более очевидный выбор в большинстве случаев.
Но я просто хочу добавить, что есть много случаев, когда LinkedList гораздо лучший выбор, чем List для лучшей эффективности.
Надеюсь, кто-то найдет эти комментарии полезными.
источник
Так много средних ответов здесь ...
Некоторые реализации связанного списка используют базовые блоки предварительно выделенных узлов. Если они этого не делают, то постоянное время / линейное время не так важно, так как производительность памяти будет низкой, а производительность кеша - еще хуже.
Используйте связанные списки, когда
1) Вы хотите безопасность потока. Вы можете создавать более безопасные многопоточные алгоритмы. Затраты на блокировку будут доминировать в списке одновременных стилей.
2) Если у вас есть большие очереди, подобные структурам, и вы хотите удалить или добавить где угодно, но конец всегда. > 100К списков существует, но не так часто.
источник
Я спросил похожий вопрос, связанный с производительностью коллекции LinkedList , и обнаружил, что реализация Deque Стивена Клири на C # была решением. В отличие от коллекции Queue, Deque позволяет перемещать предметы вперед / назад вперед и назад. Это похоже на связанный список, но с улучшенной производительностью.
источник
Deque
которое «похоже на связанный список, но с улучшенной производительностью» . Пожалуйста квалифицироваться это утверждение:Deque
лучше производительность , чемLinkedList
, для конкретного кода . Перейдя по вашей ссылке, я вижу, что через два дня вы узнали от Ивана Стоева, что это не неэффективность LinkedList, а неэффективность вашего кода. (И даже если бы это был неэффективный LinkedList, это не оправдывало бы общее утверждение, что Deque более эффективен; только в определенных случаях.)