J'essaie de créer une table de recherche de dictionnaire en C #. J'ai besoin de résoudre un 3-tuple de valeurs en une seule chaîne. J'ai essayé d'utiliser des tableaux comme clés, mais cela n'a pas fonctionné et je ne sais pas quoi faire d'autre. À ce stade, j'envisage de créer un dictionnaire de dictionnaires de dictionnaires, mais ce ne serait probablement pas très joli à regarder, même si c'est comme ça que je le ferais en javascript.
c#
dictionary
hashtable
tuples
AlexH
la source
la source
GetHashCode
implémentation n'est pas très bonne. C'est invariant sous permutation des champs.new object()
égal à un autrenew object()
? Il n'utilise pas seulement une comparaison de référence directe ... essayez:bool test = new Tuple<int, string>(1, "foo").Equals(new Tuple<int, string>(1, "Foo".ToLower()));
Entre les approches basées sur les tuple et les dictionnaires imbriqués, il est presque toujours préférable d'opter pour les tuple.
Du point de vue de la maintenabilité ,
il est beaucoup plus facile de mettre en œuvre une fonctionnalité qui ressemble à:
que
du côté de l'appelé. Dans le second cas, chaque ajout, recherche, suppression, etc. nécessite une action sur plusieurs dictionnaires.
De plus, si votre clé composite nécessite un champ de plus (ou moins) à l'avenir, vous devrez changer beaucoup de code dans le second cas (dictionnaire imbriqué) car vous devez ajouter d'autres dictionnaires imbriqués et les vérifications suivantes.
Du point de vue de la performance , la meilleure conclusion que vous puissiez tirer est de la mesurer vous-même. Mais il y a quelques limitations théoriques que vous pouvez considérer au préalable:
Dans le cas du dictionnaire imbriqué, avoir un dictionnaire supplémentaire pour chaque clé (externe et interne) entraînera une surcharge de mémoire (plus que ce que la création d'un tuple aurait).
Dans le cas du dictionnaire imbriqué, toutes les actions de base telles que l'ajout, la mise à jour, la recherche, la suppression, etc. doivent être effectuées dans deux dictionnaires. Maintenant, il y a un cas où l'approche par dictionnaire imbriqué peut être plus rapide, c'est-à-dire lorsque les données recherchées sont absentes, car les dictionnaires intermédiaires peuvent contourner le calcul et la comparaison du code de hachage complet, mais là encore, il devrait être chronométré pour être sûr. En présence de données, il devrait être plus lent car les recherches doivent être effectuées deux fois (ou trois fois selon l'imbrication).
En ce qui concerne l'approche par tuple, les tuples .NET ne sont pas les plus performants lorsqu'ils sont destinés à être utilisés comme clés dans des ensembles, car leur implémentation
Equals
et leurGetHashCode
implémentation provoquent un encadrement des types valeur .J'irais avec un dictionnaire basé sur un tuple, mais si je veux plus de performances, j'utiliserais mon propre tuple avec une meilleure implémentation.
Par ailleurs, peu de cosmétiques peuvent rendre le dictionnaire cool:
Les appels de style indexeur peuvent être beaucoup plus propres et intuitifs. Par exemple,
Exposez donc les indexeurs nécessaires dans votre classe de dictionnaire qui gère en interne les insertions et les recherches.
En outre, implémentez une
IEnumerable
interface appropriée et fournissez uneAdd(TypeA, TypeB, TypeC, string)
méthode qui vous donnerait une syntaxe d'initialisation de collection, comme:la source
string foo = dict[a][b][c]
:?a
. Vous pouvez simplement itérer n'importe quel dictionnaire comme n'importe quelle collection normale et vérifier la propriété de clé si c'est le casa
. Si vous voulez toujours obtenir l'élément dans dict par la première propriété, vous pouvez mieux concevoir le dictionnaire en tant que dictionnaire de dictionnaires comme indiqué dans ma réponse et une requête commedict[a]
, ce qui vous donne un autre dictionnaire.4
pour les deux clésa
etb
, vous pouvez en faire un dictionnaire standard et ajouter des valeurs telles quedict[a] = 4
etdict[b] = 4
. Cela peut ne pas avoir de sens si logiquement votrea
etb
devrait être une unité. Dans un tel cas, vous pouvez définir une personnalisationIEqualityComparer
qui assimile deux instants clés comme égaux si l'une de leurs propriétés est égale. Tout cela peut être fait de manière générique avec refelction.Si vous êtes sur C # 7, vous devriez envisager d'utiliser des tuples de valeur comme clé composite. Les tuples de valeur offrent généralement de meilleures performances que les tuples de référence traditionnels (
Tuple<T1, …>
) car les tuples de valeur sont des types de valeur (structs), pas des types de référence, ils évitent donc les coûts d'allocation de mémoire et de garbage collection. En outre, ils offrent une syntaxe plus concise et plus intuitive, permettant à leurs champs d'être nommés si vous le souhaitez. Ils implémentent également l'IEquatable<T>
interface nécessaire au dictionnaire.la source
Les moyens efficaces, propres, rapides, faciles et lisibles sont:
ajoutez quelque chose de similaire comme ceci:
Vous pouvez donc l'utiliser avec le dictionnaire:
la source
public TypeA DataA => Item1;
Si, pour une raison quelconque, vous voulez vraiment éviter de créer votre propre classe Tuple ou d'utiliser une fonction intégrée à .NET 4.0, il existe une autre approche possible; vous pouvez combiner les trois valeurs clés en une seule valeur.
Par exemple, si les trois valeurs sont des types entiers ensemble ne prenant pas plus de 64 bits, vous pouvez les combiner en un fichier
ulong
.Dans le pire des cas, vous pouvez toujours utiliser une chaîne, tant que vous vous assurez que les trois composants sont délimités par un caractère ou une séquence qui ne se produit pas à l'intérieur des composants de la clé, par exemple, avec trois nombres, vous pouvez essayer:
Il y a évidemment une surcharge de composition dans cette approche, mais en fonction de ce que vous l'utilisez, cela peut être assez trivial pour ne pas s'en soucier.
la source
JavaScriptSerializer
pour concaténer un tableau de types chaîne et / ou entier pour vous. De cette façon, vous n'avez pas besoin de créer vous-même un caractère délimiteur.key1
,key2
,key3
) sont des chaînes contenant le deliminator ("#"
)Je remplacerais votre Tuple avec un GetHashCode approprié, et l'utiliserais simplement comme clé.
Tant que vous surchargez les méthodes appropriées, vous devriez voir des performances décentes.
la source
Voici le tuple .NET pour référence:
la source
Si votre code consommateur peut se contenter d'une interface IDictionary <>, au lieu de Dictionary, mon instinct aurait été d'utiliser un SortedDictionary <> avec un comparateur de tableau personnalisé, c'est-à-dire:
Et créez ainsi (en utilisant int [] juste pour des exemples concrets):
la source