web-dev-qa-db-fra.com

Structures de données .NET: ArrayList, List, HashTable, Dictionnaire, SortedList, SortedDictionary - Vitesse, mémoire et quand les utiliser?

.NET a beaucoup de structures de données complexes. Malheureusement, certains d'entre eux sont assez similaires et je ne sais pas toujours quand utiliser l'un ou l'autre. La plupart de mes livres C # et Visual Basic en parlent dans une certaine mesure, mais ils n'entrent jamais vraiment dans les détails.

Quelle est la différence entre Array, ArrayList, List, Hashtable, Dictionary, SortedList et SortedDictionary?

Lesquels sont énumérables (IList - peut faire des boucles 'pour chaque')? Lesquels utilisent des paires clé/valeur (IDict)?

Qu'en est-il de l'empreinte mémoire? Vitesse d'insertion? La vitesse de récupération?

Existe-t-il d'autres structures de données qui valent la peine d'être mentionnées?

Je cherche toujours plus de détails sur l'utilisation de la mémoire et sa vitesse (notation Big-O).

209
Pretzel

Du haut de ma tête:

  • Array * - représente un tableau de mémoire old-school, un peu comme un alias pour un tableau type[] normal. Peut énumérer. Ne peut pas grandir automatiquement. Je suppose que la vitesse d'insertion et de retour est très rapide.

  • ArrayList - tableau à croissance automatique. Ajoute plus de frais généraux. Can enum., Probablement plus lent qu'un tableau normal mais quand même assez rapide. Ceux-ci sont beaucoup utilisés dans .NET

  • List - un de mes favoris - peut être utilisé avec des génériques, vous pouvez donc avoir un tableau fortement typé, par exemple. List<string>. Autre que cela, agit très bien comme ArrayList

  • Hashtable - hashtable simple et vieux. O(1) à O(n) pire des cas. Peut énumérer les propriétés value et keys, et faire des paires key/val

  • Dictionary - comme ci-dessus, mais uniquement avec des caractères génériques, tels que Dictionary<string, string>

  • SortedList - une liste générique triée. Ralentit lors de l'insertion car il doit trouver où mettre les choses. Can enum., Probablement la même chose lors de la récupération car il n'a pas à recourir, mais la suppression sera plus lente qu'une liste ancienne.

J'ai tendance à utiliser List et Dictionary tout le temps - une fois que vous commencez à les utiliser fortement typés avec des génériques, il est vraiment difficile de revenir aux standards non génériques.

Il y a beaucoup d'autres structures de données aussi - il y a KeyValuePair que vous pouvez utiliser pour faire des choses intéressantes, il y a un SortedDictionary qui peut aussi être utile.

148
Sam Schutte

Si possible, utilisez des génériques. Cela inclut:

  • List au lieu de ArrayList
  • Dictionnaire au lieu de HashTable
28
Adam Tegen

Tout d'abord, toutes les collections de .NET implémentent IEnumerable.

Deuxièmement, beaucoup de collections sont des doublons car des génériques ont été ajoutés à la version 2.0 du framework.

Ainsi, bien que les collections génériques ajoutent probablement des fonctionnalités, dans la plupart des cas:

  • List est une implémentation générique de ArrayList.
  • Dictionary est une implémentation générique de Hashtable.

Les tableaux sont une collection de taille fixe dans laquelle vous pouvez modifier la valeur stockée à un index donné.

SortedDictionary est un IDictionary qui est trié en fonction des clés. SortedList est un IDictionary qui est trié en fonction d'un IComparer requis.

Ainsi, les implémentations IDictionary (celles qui prennent en charge KeyValuePairs) sont les suivantes: * Hashtable * Dictionary * SortedList * SortedDictionary

Le hachage est une autre collection ajoutée dans .NET 3.5. C'est une collection qui prend en charge les opérations sur les ensembles.

De plus, LinkedList est une implémentation standard de la liste chaînée (List est une liste de type array pour une récupération plus rapide).

24
Abe Heidebrecht

n bon aide-mémoire mentionnant la complexité des structures de données, des algorithmes, etc.

21
Krishna

Voici quelques conseils généraux pour vous:

  • Vous pouvez utiliser foreach sur les types qui implémentent IEnumerable. IList est essentiellement une propriété IEnumberable avec Count et Item (accès aux éléments à l'aide d'un index de base zéro). IDictionary d'autre part signifie que vous pouvez accéder aux éléments à l'aide d'un index non chiffrable.

  • Array, ArrayList et List tous implémentent IList. Dictionary, SortedDictionary et Hashtable implémente IDictionary.

  • Si vous utilisez .NET 2.0 ou une version ultérieure, il est recommandé d'utiliser des équivalents génériques des types mentionnés.

  • Pour connaître la complexité en temps et en espace des diverses opérations effectuées sur ces types, consultez leur documentation.

  • Les structures de données .NET sont dans l'espace de noms System.Collections. Il existe des bibliothèques de types telles que PowerCollections qui offrent des structures de données supplémentaires.

  • Pour bien comprendre les structures de données, consultez des ressources telles que CLRS .

18
blackwing

Structures de données .NET:

En savoir plus sur les raisons pour lesquelles ArrayList et List sont en réalité différents

Tableaux

Comme le dit un utilisateur, les tableaux sont la collection "old school" (oui, les tableaux sont considérés comme une collection bien qu'ils ne fassent pas partie de System.Collections). Mais qu’est-ce que la vieille école représente les tableaux par rapport à d’autres collections, c’est-à-dire celles que vous avez énumérées dans votre titre (ici, ArrayList et List (Of T))? Commençons par les bases en regardant Arrays.

Pour commencer, Tableaux dans Microsoft .NET sont des "mécanismes qui vous permettent de traiter plusieurs éléments [liés logiquement] comme une seule et même collection" (voir article lié). Qu'est-ce que ça veut dire? Les tableaux stockent les membres individuels (éléments) de manière séquentielle, les uns après les autres, en mémoire avec une adresse de départ. En utilisant le tableau, nous pouvons facilement accéder aux éléments séquentiellement stockés commençant à cette adresse.

Au-delà de cela et contrairement à la programmation 101 conceptions communes, les tableaux peuvent être très complexes:

Les tableaux peuvent être à une dimension, multidimensionnels ou jaddés (les tableaux déchiquetés valent la peine d'être lus). Les tableaux eux-mêmes ne sont pas dynamiques: une fois initialisé, un tableau de taille n réserve suffisamment d'espace pour contenir n nombre d'objets. Le nombre d'éléments dans le tableau ne peut pas augmenter ou diminuer. Dim _array As Int32() = New Int32(100) réserve assez d'espace sur le bloc de mémoire pour que le tableau contienne 100 objets de type primitif Int32 (dans ce cas, le tableau est initialisé pour contenir des 0). L'adresse de ce bloc est renvoyée à _array.

Selon l'article, Common Language Specification (CLS) requiert que tous les tableaux soient à base zéro. Les tableaux dans .NET prennent en charge les tableaux à base non nulle; Cependant, cela est moins commun. En raison de la "banalisation" des baies de base zéro, Microsoft a passé beaucoup de temps à optimiser ses performances ; Par conséquent, les tableaux à une seule dimension et à base zéro (SZ) sont "spéciaux" - et constituent en fait la meilleure implémentation d'un tableau (par opposition à un système multidimensionnel, etc.) - car les ZZ disposent d'instructions de langage intermédiaire spécifiques pour les manipuler.

Les tableaux sont toujours passés par référence (en tant qu’adresse mémoire) - une pièce importante du puzzle Array à connaître. Bien qu'ils vérifient les limites (généreront une erreur), la vérification des limites peut également être désactivée sur les tableaux.

Encore une fois, le plus gros obstacle aux tableaux est qu’ils ne sont pas redimensionnables. Ils ont une capacité "fixe". Introduction de ArrayList et List (Of T) à notre histoire:

ArrayList - liste non générique

Le ArrayList (avec List(Of T) - bien qu'il y ait des différences critiques, ici, expliqué plus loin) - est peut-être préférable de le considérer comme le prochain ajout aux collections (au sens large). ArrayList hérite de l'interface IList (un descendant de 'ICollection'). Les listes de tableaux, elles-mêmes, sont plus volumineuses - nécessitent plus de frais généraux - que les listes.

IList permet à l'implémentation de traiter ArrayLists comme des listes de taille fixe (comme des tableaux); Cependant, au-delà de la fonctionnalité supplémentaire ajoutée par ArrayLists, l'utilisation d'ArrayLists de taille fixe, car ArrayLists (sur Arrays), dans ce cas, est nettement plus lente.

D'après ma lecture, ArrayLists ne peut pas être déchiqueté: "L'utilisation de tableaux multidimensionnels en tant qu'éléments ... n'est pas prise en charge". Encore une fois, un autre clou dans le cercueil de ArrayLists. Les tableaux ne sont pas non plus "typés" - ce qui signifie qu'en dessous de tout, un tableau est simplement un tableau dynamique d'objets: Object[]. Cela nécessite beaucoup de boxe (implicite) et unboxing (explicite) lors de la mise en œuvre de ArrayLists, ce qui ajoute encore à leur surcharge.

Pensée non corroborée: je pense me souvenir d'avoir lu ou d'avoir entendu de l'un de mes professeurs que ArrayLists est en quelque sorte l'enfant conceptuel bâtard de la tentative de passer de tableaux à des collections de type liste, c'est-à-dire Une grande amélioration par rapport aux tableaux, ils ne sont plus la meilleure option car un développement ultérieur a été fait en ce qui concerne les collections

List (Of T): Qu'est-ce qu'ArrayList est devenu (et espérait être)

La différence d'utilisation de la mémoire est assez significative pour qu'un List (Of Int32) consomme 56% moins de mémoire qu'un ArrayList contenant le même type primitif (8 Mo par rapport à 19 Mo dans la démonstration liée du gentleman ci-dessus: à nouveau, lié ici ) - bien qu’il s’agisse d’un résultat aggravé par la machine 64 bits. Cette différence montre bien deux choses: premièrement (1), un "objet" de type Int32 encadré (ArrayList) est beaucoup plus gros qu'un type de primitive Int32 pur (List); seconde (2), la différence est exponentielle du fait du fonctionnement interne d’une machine 64 bits.

Alors, quelle est la différence et qu'est-ce qu'un List (Of T) ? MSDN définit une List(Of T) comme: "... une liste fortement typée d'objets accessibles par index." L'importance ici est le bit "fortement typé": une liste (de T) "reconnaît" les types et stocke les objets sous leur type. Ainsi, un Int32 est stocké sous le type Int32 et non de type Object. Cela élimine les problèmes causés par la boxe et le déballage.

MSDN spécifie que cette différence n’entre en jeu que lors du stockage de types primitifs et non de types de référence. De plus, la différence se produit réellement à grande échelle: plus de 500 éléments. Le plus intéressant est que la documentation MSDN se lit comme suit: "Il est avantageux d'utiliser l'implémentation spécifique à un type de la classe List (Of T) plutôt que d'utiliser la classe ArrayList ...."

Essentiellement, List (Of T) est ArrayList, mais meilleur. C'est "l'équivalent générique" de ArrayList. Comme ArrayList, il n’est pas garanti qu’il soit trié jusqu’à trié (voir figure). List (Of T) a également quelques fonctionnalités ajoutées.

7
Thomas

Je comprends la question - moi aussi j'ai trouvé (trouver?) Le choix déroutant, alors je me suis lancé scientifiquement pour voir quelle structure de données était la plus rapide (j'ai fait le test avec VB, mais j'imagine que C # serait identique, car les deux langages faire la même chose au niveau CLR). Vous pouvez voir certains résultats d'analyse comparative que j'ai menés ici (il y a également une discussion sur le type de données qu'il est préférable d'utiliser dans quelles circonstances).

5
Andy Brown

Les collections génériques fonctionneront mieux que leurs homologues non génériques, en particulier lors de l'itération de nombreux éléments. C'est parce que la boxe et le déballage ne se produisent plus.

3
Russ Cam

Les tables de hachage/dictionnaires sont O(1) performances, ce qui signifie que les performances ne dépendent pas de la taille. C'est important à savoir.

EDIT: En pratique, la complexité temporelle moyenne pour les recherches Hashtable/Dictionary <> est de O (1).

3
Chris

Ils sont assez bien expliqués en intellisense. Il suffit de taper System.Collections. ou System.Collections.Generics (préféré) et vous obtiendrez une liste et une courte description de ce qui est disponible.

3
Joel Coehoorn

Remarque importante sur Hashtable vs Dictionary pour l'ingénierie du trading systématique à haute fréquence: Problème de sécurité des threads

Hashtable est thread-safe pour une utilisation par plusieurs threads. Les membres statiques publics du dictionnaire sont thread-safe, mais aucun membre d'instance n'est garanti d'être ainsi.

Hashtable reste donc le choix "standard" à cet égard.

2
Rob

Structures et collections de données C # les plus populaires

  • Tableau
  • ArrayList
  • Liste
  • LinkedList
  • Dictionnaire
  • HashSet
  • Stack
  • File d'attente
  • SortedList

C # .NET a beaucoup de structures de données différentes, par exemple, l'une des plus courantes est un tableau. Cependant, C # est livré avec beaucoup plus de structures de données de base. Le choix de la structure de données appropriée à utiliser fait partie de l’écriture d’un programme bien structuré et efficace.

Dans cet article, je passerai en revue les structures de données intégrées C #, y compris les nouvelles introduites dans C # .NET 3.5. Notez que beaucoup de ces structures de données s'appliquent à d'autres langages de programmation.

Tableau

La structure de données peut-être la plus simple et la plus courante est le tableau. Un tableau C # est fondamentalement une liste d'objets. Ses traits caractéristiques sont que tous les objets sont du même type (dans la plupart des cas) et qu'il en existe un nombre spécifique. La nature d'un tableau permet un accès très rapide aux éléments en fonction de leur position dans la liste (également appelé index). Un tableau C # est défini comme ceci:

[object type][] myArray = new [object type][number of elements]

Quelques exemples:

 int[] myIntArray = new int[5];
 int[] myIntArray2 = { 0, 1, 2, 3, 4 };

Comme vous pouvez le voir dans l'exemple ci-dessus, un tableau peut être initialisé sans élément ou à partir d'un ensemble de valeurs existantes. L'insertion de valeurs dans un tableau est simple tant qu'elles y correspondent. L'opération devient coûteuse lorsqu'il y a plus d'éléments que la taille du tableau, auquel cas le tableau doit être développé. Cela prend plus de temps car tous les éléments existants doivent être copiés dans le nouveau tableau plus grand.

ArrayList

La structure de données C #, ArrayList, est un tableau dynamique. Cela signifie qu'un ArrayList peut avoir n'importe quelle quantité d'objets et de n'importe quel type. Cette structure de données a été conçue pour simplifier les processus d’ajout de nouveaux éléments dans un tableau. Sous le capot, un ArrayList est un tableau dont la taille est doublée chaque fois qu'il manque d'espace. Doubler la taille du tableau interne est une stratégie très efficace qui réduit le nombre de copies d'éléments à long terme. Nous n'entrerons pas dans la preuve de cela ici. La structure de données est très simple à utiliser:

    ArrayList myArrayList = new ArrayList();
    myArrayList.Add(56);
    myArrayList.Add("String");
    myArrayList.Add(new Form());

L'inconvénient de la structure de données ArrayList est qu'il faut reconvertir les valeurs reçues dans leur type d'origine:

int arrayListValue = (int)myArrayList[0]

Sources et informations complémentaires, vous trouverez ici :

1
leonidaa

En fait, je pense que MSDN aide à fournir de très bonnes réponses à toutes ces questions. Il suffit de regarder les collections .NET.

1
Scott

Il existe des différences subtiles et pas si subtiles entre les collections génériques et non génériques. Ils utilisent simplement différentes structures de données sous-jacentes. Par exemple, Hashtable garantit un écrivain, plusieurs lecteurs sans synchronisation. Le dictionnaire ne le fait pas.

1
Ilya Ryzhenkov