Efficacité des dictionnaires C #

14

Les dictionnaires C # sont un moyen simple de trouver si quelque chose existe, etc. Je me demande cependant comment ils fonctionnent. Disons qu'au lieu d'un dictionnaire, j'utilise une ArrayList. Au lieu d'utiliser ContainsKey(ou une méthode équivalente dans une autre langue), je passe en revue ArrayList pour vérifier si quelque chose existe (ou effectuer une recherche binaire si les données sont triées ou quelque chose de similaire). Quelle est la différence d'efficacité? La ContainsKeyméthode utilise-t-elle un moyen plus efficace plutôt que de parcourir les clés et de vérifier si ce que je recherche existe?

Si disons que j'avais créé une fonction de hachage spécifique qui correspond au type de données que j'ai et qui est spécifiquement conçue pour cet ensemble de données, alors oui, cette fonction de hachage est en effet plus rapide que de parcourir les données. Mais les dictionnaires sont généraux. La méthode ContainsKey n'est pas spécifique aux données qu'elle obtient, c'est une méthode de recherche générale.

Fondamentalement, ce que je demande, c'est. Les dictionnaires sont utiles aux programmeurs. Ils incluent des méthodes qui aident à beaucoup de choses et ils combinent des chaînes avec des entiers, (clés et valeurs) et bien d'autres. Mais en matière d'efficacité, qu'offrent-ils? Quelle est la différence d'avoir un dictionarycontre un ArrayListdestructs(string,int)

John Demetriou
la source
Vous comparez vraiment des pommes avec des oranges ici. Je pense que le mot clé que vous recherchez est Data Structures ce lien wiki peut vous être plus utile
Ampt

Réponses:

22

Vous devez creuser un peu pour voir comment le dictionnaire est implémenté en C # - Ce n'est pas aussi évident que HashMap (une table de hachage) ou TreeMap (une arborescence triée) (ou ConcurrentSkipListMap - une liste de saut ).

Si vous creusez dans la section "Remarques":

La classe générique Dictionary fournit un mappage d'un ensemble de clés à un ensemble de valeurs. Chaque ajout au dictionnaire se compose d'une valeur et de sa clé associée. La récupération d'une valeur à l'aide de sa clé est très rapide, proche de O (1), car la classe Dictionary est implémentée comme une table de hachage.

Et nous l'avons. C'est une table de hachage . Notez que j'ai lié l'article Wikipédia là-bas - c'est une assez bonne lecture. Vous voudrez peut-être lire la section sur la résolution des collisions. Il est possible d'obtenir un ensemble de données pathologiques où la recherche revient à O (N) (par exemple, tout ce que vous insérez tombe dans la même valeur de hachage ou index dans la table de hachage pour une raison quelconque et vous vous retrouvez avec un sondage linéaire ).

Bien que le dictionnaire soit une solution à usage général, vous ne devez pas contourner les types concrets (tels que le dictionnaire) - vous devez contourner les interfaces. Dans ce cas, cette interface est IDictionary( docs ). Pour cela, vous êtes parfaitement capable d'écrire votre propre implémentation de dictionnaire qui fait les choses de manière optimale pour les données dont vous disposez.

Quant à l'efficacité de la recherche / contient divers?

  • Parcourir une liste non triée: O (N)
  • Recherche binaire d'un tableau trié: O (log N)
  • Arbre trié: O (log N)
  • Table de hachage: O (1)

Pour la plupart des gens, la table de hachage est ce qu'ils veulent.

Vous pouvez trouver que le SortedDictionary est ce que vous voulez à la place:

La SortedDictionary<TKey, TValue>classe générique est un arbre de recherche binaire avec récupération O (log n), où n est le nombre d'éléments dans le dictionnaire. À cet égard, elle est similaire à la SortedList<TKey, TValue>classe générique. Les deux classes ont des modèles d'objet similaires et les deux ont une récupération O (log n).

Bien que, encore une fois, si la structure de données ne fonctionne pas idéalement avec vos données, vous disposez des outils (les interfaces) pour pouvoir en écrire un qui fonctionne le mieux pour vos données.

Le dictionnaire lui-même est un type de données abstrait . Vous me donnez un dictionnaire et je sais ce que je peux en faire et tous les outils là-bas pour moi d'utiliser par la nature d'être un dictionnaire. Si vous me donniez une liste de tableaux, je me retrouverais à écrire mon propre code pour rechercher, insérer ou supprimer des éléments de la liste. Cela me fait perdre du temps et signifie également qu'il y a plus de chances pour un bug que je copie le code encore et encore d'un endroit à l'autre.

Robert Harvey
la source
5
O (1) n'est pas nécessairement "rapide". Le fait de parcourir une liste pourrait être plus rapide qu'une table de hachage pour les tailles de collection auxquelles l'application est confrontée.
whatsisname
5
@whatsisname à aucun moment je ne prétends que O (1) est rapide. Il a certainement le potentiel d'être le plus rapide. L'itération sur les clés d'une table de hachage est plus lente que celle d'une liste de tableaux (sauf si vous utilisez quelque chose comme LinkedHashMap fourni par Java). Il est important de connaître vos données et leur comportement et de choisir la collection appropriée pour elles - et si cela n'existe pas, écrivez-les. En supposant, bien sûr, qu'une telle entreprise en vaille la peine (profil d'abord!).
Votre citation dit "La récupération d'une valeur en utilisant sa clé est très rapide, proche de O (1), car la classe Dictionary est implémentée comme une table de hachage.", Donc l'OP pourrait confondre les deux concepts. En d'autres termes, je voulais préciser que le grand O ne raconte pas toute l'histoire concernant la "vitesse".
whatsisname
3
@whatsisname qui provient directement de Microsoft. L'utilisation d'une clé pour rechercher une valeur, à moins que vous n'ayez une table de hachage pathologique (qui résout les collisions de hachage avec un autre mécanisme) sera plus rapide que de la rechercher dans une arborescence ou une liste triée (ou une liste non triée). Java, par exemple, utilise le sondage linéaire (étape 1) pour sa résolution de collision - qui peut être plus lente dans les cas où la table est trop pleine ou trop de hachages entrent en collision. Pour le cas général cependant, c'est assez bon.
À titre d'exemple pertinent, j'ai récemment optimisé du code en c ++ qui utilisait à l'origine une table de hachage pour des ensembles de données d'environ 20 entrées et prenait environ 400 ms à terminer. Le passage à un arbre binaire a réduit cela à 200 ms, car l'arbre est plus simple d'accès. Mais j'ai pu le réduire encore plus en utilisant un tableau de paires de valeurs de nom et une fonction de recherche heuristique qui a deviné où commencer à chercher en fonction des modèles d'accès passés. C'est donc une question de quantité de données et de types de modèles dans les accès (par exemple, la localité).
Jules