Quand est-il préférable d'utiliser une liste par rapport à une LinkedList ?
c#
.net
vb.net
data-structures
linked-list
Jonathan Allen
la source
la source
Réponses:
Éditer
Réponse originale ...
J'ai trouvé des résultats intéressants:
Liste liée (3,9 secondes)
Liste (2,4 secondes)
Même si vous accédez uniquement aux données, c'est beaucoup plus lent !! Je dis de ne jamais utiliser une LinkedList.
Voici une autre comparaison effectuant beaucoup d'insertions (nous prévoyons d'insérer un élément au milieu de la liste)
Liste liée (51 secondes)
Liste (7,26 secondes)
Liste liée avec référence de l'emplacement où insérer (0,04 seconde)
Donc, seulement si vous prévoyez d'insérer plusieurs éléments et que vous avez également quelque part la référence de l'endroit où vous prévoyez d'insérer l'élément, utilisez une liste liée. Tout simplement parce que vous devez insérer beaucoup d'éléments, cela ne le rend pas plus rapide, car la recherche de l'emplacement où vous souhaitez l'insérer prend du temps.
la source
list.AddLast(a);
dans les deux derniers exemples LinkedList? Je le fais une fois avant la boucle, commelist.AddLast(new Temp(1,1,1,1));
dans l'avant-dernière LinkedList, mais il me semble que vous ajoutez deux fois plus d'objets Temp dans les boucles elles-mêmes. (Et quand je me revérifie avec une application de test , bien sûr, deux fois plus dans la LinkedList.)I say never use a linkedList.
sont imparfaits comme le révèle votre post ultérieur. Vous voudrez peut-être le modifier. 2) Quel est le timing? Instanciation, addition et dénombrement en une seule étape? Généralement, l'instanciation et l'énumération ne sont pas ce qui inquiète ppl, ce sont des étapes uniques. Plus précisément, la synchronisation des insertions et des ajouts donnerait une meilleure idée. 3) Plus important encore, vous ajoutez plus que nécessaire à une liste de liens. C'est une mauvaise comparaison. Propage une mauvaise idée de la liste de liens.Dans la plupart des cas,
List<T>
est plus utile.LinkedList<T>
aura moins de coût lors de l'ajout / suppression d'éléments au milieu de la liste, alors que vousList<T>
pouvez seulement ajouter / supprimer à moindre coût à la fin de la liste.LinkedList<T>
n'est efficace que si vous accédez à des données séquentielles (en avant ou en arrière) - l'accès aléatoire est relativement cher car il doit parcourir la chaîne à chaque fois (d'où la raison pour laquelle il n'a pas d'indexeur). Cependant, comme aList<T>
n'est essentiellement qu'un tableau (avec un wrapper), l'accès aléatoire est correct.List<T>
offre également un grand nombre de méthodes de soutien -Find
,ToArray
, etc; cependant, ceux-ci sont également disponibles pourLinkedList<T>
.NET 3.5 / C # 3.0 via des méthodes d'extension - c'est donc moins un facteur.la source
List<T>
etT[]
échouera pour être trop gros (tous une dalle),LinkedList<T>
se lamentera pour être trop granulaire (dalle par élément).Penser une liste chaînée comme une liste peut être un peu trompeur. C'est plus comme une chaîne. En fait, dans .NET,
LinkedList<T>
ne met même pas en œuvreIList<T>
. Il n'y a pas de véritable concept d'index dans une liste chaînée, même si cela peut sembler être le cas. Certes, aucune des méthodes fournies dans la classe n'accepte les index.Les listes liées peuvent être liées individuellement ou doublement liées. Il s'agit de savoir si chaque élément de la chaîne a un lien uniquement avec le suivant (lié individuellement) ou avec les deux éléments antérieurs / suivants (liés deux fois).
LinkedList<T>
est doublement lié.En interne,
List<T>
est soutenu par un tableau. Cela fournit une représentation très compacte en mémoire. Inversement,LinkedList<T>
implique une mémoire supplémentaire pour stocker les liens bidirectionnels entre les éléments successifs. Ainsi, l'empreinte mémoire d'unLinkedList<T>
sera généralement plus grande que pourList<T>
(avec la mise en garde quiList<T>
peut avoir des éléments de tableau interne inutilisés pour améliorer les performances lors des opérations d'ajout.)Ils ont également des caractéristiques de performance différentes:
Ajouter
LinkedList<T>.AddLast(item)
temps constantList<T>.Add(item)
temps constant amorti, pire cas linéairePrepend
LinkedList<T>.AddFirst(item)
temps constantList<T>.Insert(0, item)
temps linéaireInsertion
LinkedList<T>.AddBefore(node, item)
temps constantLinkedList<T>.AddAfter(node, item)
temps constantList<T>.Insert(index, item)
temps linéaireSuppression
LinkedList<T>.Remove(item)
temps linéaireLinkedList<T>.Remove(node)
temps constantList<T>.Remove(item)
temps linéaireList<T>.RemoveAt(index)
temps linéaireCompter
LinkedList<T>.Count
temps constantList<T>.Count
temps constantContient
LinkedList<T>.Contains(item)
temps linéaireList<T>.Contains(item)
temps linéaireClair
LinkedList<T>.Clear()
temps linéaireList<T>.Clear()
temps linéaireComme vous pouvez le voir, ils sont pour la plupart équivalents. En pratique, l'API de
LinkedList<T>
est plus lourde à utiliser et les détails de ses besoins internes se répandent dans votre code.Cependant, si vous devez effectuer de nombreuses insertions / suppressions à partir d'une liste, elle offre un temps constant.
List<T>
offre un temps linéaire, car les éléments supplémentaires de la liste doivent être mélangés après l'insertion / la suppression.la source
Les listes liées permettent l'insertion ou la suppression très rapide d'un membre de liste. Chaque membre d'une liste chaînée contient un pointeur sur le membre suivant de la liste afin d'insérer un membre à la position i:
L'inconvénient d'une liste chaînée est qu'un accès aléatoire n'est pas possible. L'accès à un membre nécessite de parcourir la liste jusqu'à ce que le membre souhaité soit trouvé.
la source
Ma réponse précédente n'était pas assez précise. C'était vraiment horrible: D Mais maintenant je peux poster une réponse beaucoup plus utile et correcte.
J'ai fait quelques tests supplémentaires. Vous pouvez trouver sa source par le lien suivant et la vérifier à nouveau dans votre environnement: https://github.com/ukushu/DataStructuresTestsAndOther.git
Résultats courts:
Le tableau doit utiliser:
La liste doit utiliser:
LinkedList doit utiliser:
Plus de détails:
Intéressant à savoir:
LinkedList<T>
en interne n'est pas une liste dans .NET. Il n'est même pas implémentéIList<T>
. Et c'est pourquoi il existe des index et des méthodes absents liés aux index.LinkedList<T>
est une collection basée sur un pointeur de nœud. Dans .NET, il s'agit d'une implémentation doublement liée. Cela signifie que les éléments antérieurs / suivants ont un lien avec l'élément actuel. Et les données sont fragmentées - différents objets de liste peuvent être situés à différents endroits de la RAM. Il y aura également plus de mémoire utilisée pourLinkedList<T>
que pourList<T>
ou Array.List<T>
in .Net est l'alternative de Java àArrayList<T>
. Cela signifie qu'il s'agit d'un wrapper de tableau. Il est donc alloué en mémoire comme un bloc de données contigu. Si la taille des données allouées dépasse 85 000 octets, elles seront déplacées vers le grand segment d'objets. Selon la taille, cela peut entraîner une fragmentation du tas (une forme légère de fuite de mémoire). Mais en même temps, si la taille est <85000 octets - cela fournit une représentation très compacte et à accès rapide en mémoire.Un bloc contigu unique est préféré pour les performances d'accès aléatoire et la consommation de mémoire, mais pour les collections qui doivent changer régulièrement de taille, une structure telle qu'un tableau doit généralement être copiée vers un nouvel emplacement, tandis qu'une liste liée ne doit gérer la mémoire que pour la nouvelle insertion. / noeuds supprimés.
la source
La différence entre List et LinkedList réside dans leur implémentation sous-jacente. La liste est une collection basée sur un tableau (ArrayList). LinkedList est une collection basée sur un pointeur de nœud (LinkedListNode). Sur l'utilisation au niveau de l'API, les deux sont à peu près les mêmes, car les deux implémentent le même ensemble d'interfaces telles que ICollection, IEnumerable, etc.
La principale différence survient lorsque les performances sont importantes. Par exemple, si vous implémentez la liste qui comporte une opération lourde "INSERT", LinkedList surpasse la liste. Étant donné que LinkedList peut le faire en temps O (1), mais List peut avoir besoin d'étendre la taille du tableau sous-jacent. Pour plus d'informations / détails, vous voudrez peut-être lire la différence algorithmique entre LinkedList et les structures de données de tableau. http://en.wikipedia.org/wiki/Linked_list et Array
J'espère que cette aide,
la source
Add
est toujours à la fin du tableau existant.List
est "assez bon" à cela, même si ce n'est pas O (1). Le problème grave se produit si vous avez besoin de plusieursAdd
s qui ne sont pas à la fin. Marc souligne que la nécessité de déplacer les données existantes à chaque fois que vous insérez (et pas seulement lorsqu'un redimensionnement est nécessaire) représente un coût de performance plus importantList
.Le principal avantage des listes liées par rapport aux tableaux est que les liens nous permettent de réorganiser efficacement les éléments. Sedgewick, p. 91
la source
Une circonstance courante pour utiliser LinkedList est comme ceci:
Supposons que vous souhaitiez supprimer plusieurs chaînes de caractères d'une liste de chaînes de grande taille, disons 100 000. Les chaînes à supprimer peuvent être consultées dans le dic HashSet, et la liste des chaînes contient entre 30 000 et 60 000 chaînes à supprimer.
Alors, quel est le meilleur type de liste pour stocker les 100 000 cordes? La réponse est LinkedList. Si elles sont stockées dans une liste de tableaux, itérer dessus et supprimer les chaînes correspondantes devrait prendre jusqu'à des milliards d'opérations, alors qu'il ne faut que 100000 opérations en utilisant un itérateur et la méthode remove ().
la source
RemoveAll
pour supprimer les éléments d'unList
sans déplacer beaucoup d'éléments, ou utiliserWhere
de LINQ pour créer une deuxième liste.LinkedList
Cependant, utiliser un ici finit par consommer beaucoup plus de mémoire que les autres types de collections et la perte de localité de mémoire signifie qu'il sera beaucoup plus lent à itérer, ce qui le rendra bien pire qu'un aList
.RemoveAll
équivalent en Java.RemoveAll
n'est pas disponible pourList
, vous pourriez faire un algorithme de «compactage», qui ressemblerait à la boucle de Tom, mais avec deux indices et la nécessité de déplacer les éléments à conserver un par un dans le tableau interne de la liste. L'efficacité est O (n), identique à l'algorithme de Tom pourLinkedList
. Dans les deux versions, le temps de calculer la clé HashSet pour les chaînes domine. Ce n'est pas un bon exemple de quand l'utiliserLinkedList
.Lorsque vous avez besoin d'un accès indexé intégré, d'un tri (et après cette recherche binaire) et d'une méthode "ToArray ()", vous devez utiliser List.
la source
Essentiellement, un
List<>
dans .NET est un wrapper sur un tableau . ALinkedList<>
est une liste chaînée . Donc, la question se résume à, quelle est la différence entre un tableau et une liste liée, et quand un tableau doit-il être utilisé à la place d'une liste liée. Probablement les deux facteurs les plus importants dans votre décision à utiliser se résumeraient à:la source
Ceci est adapté de la réponse acceptée de Tono Nam en y corrigeant quelques mesures erronées.
Le test:
Et le code:
Vous pouvez voir que les résultats sont conformes aux performances théoriques que d'autres ont documentées ici. Assez clair -
LinkedList<T>
gagne beaucoup de temps en cas d'insertions. Je n'ai pas testé la suppression du milieu de la liste, mais le résultat devrait être le même. Bien sûr, ilList<T>
existe d'autres domaines où il fonctionne bien mieux comme l'accès aléatoire O (1).la source
Utiliser
LinkedList<>
quandToken Stream
,.Pour tout le reste, il vaut mieux l'utiliser
List<>
.la source
LinkedListNode<T>
objets dans votre code. Si vous pouvez le faire, c'est bien mieux que d'utiliserList<T>
, en particulier pour les très longues listes où les insertions / suppressions sont fréquentes.node.Value
quand vous voulez l'élément d'origine). Vous réécrivez donc l'algorithme pour travailler avec des nœuds, pas avec des valeurs brutes.Je suis d'accord avec la plupart des arguments avancés ci-dessus. Et je suis également d'accord que List semble être un choix plus évident dans la plupart des cas.
Mais, je veux juste ajouter qu'il existe de nombreux cas où LinkedList est un bien meilleur choix que List pour une meilleure efficacité.
J'espère que quelqu'un trouverait ces commentaires utiles.
la source
Tant de réponses moyennes ici ...
Certaines implémentations de liste chaînée utilisent des blocs sous-jacents de nœuds préalloués. S'ils ne le font pas, le temps constant / temps linéaire est moins pertinent car les performances de la mémoire seront médiocres et les performances du cache seront encore pires.
Utilisez des listes chaînées lorsque
1) Vous voulez la sécurité des fils. Vous pouvez créer de meilleurs algos sûrs pour les threads. Les coûts de verrouillage domineront une liste de styles simultanés.
2) Si vous avez une grande file d'attente comme des structures et que vous souhaitez supprimer ou ajouter n'importe où mais la fin tout le temps. > 100K listes existent mais ne sont pas si courantes.
la source
J'ai posé une question similaire concernant les performances de la collection LinkedList et j'ai découvert que l'implémentation C # de Steven Cleary de Deque était une solution. Contrairement à la collection Queue, Deque permet de déplacer des éléments vers l'avant et l'arrière. Il est similaire à la liste chaînée, mais avec des performances améliorées.
la source
Deque
est "similaire à la liste chaînée, mais avec des performances améliorées" . Veuillez qualifier cette déclaration:Deque
est une meilleure performance queLinkedList
, pour votre code spécifique . Suite à votre lien, je vois que deux jours plus tard, vous avez appris d'Ivan Stoev que ce n'était pas une inefficacité de LinkedList, mais une inefficacité de votre code. (Et même s'il s'agissait d'une inefficacité de LinkedList, cela ne justifierait pas une déclaration générale selon laquelle Deque est plus efficace; uniquement dans des cas spécifiques.)