Tuples (ou tableaux) en tant que clés de dictionnaire en C #

J’essaie de créer une table de consultation de dictionnaire en C #. Je dois résoudre un sortingplet de valeurs en une 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, mais c’est ce que je ferais en javascript.

Si vous êtes sur .NET 4.0, utilisez un Tuple:

lookup = new Dictionary, ssortingng>(); 

Sinon, vous pouvez définir un Tuple et l’utiliser comme clé. Le Tuple doit remplacer GetHashCode, Equals et IEquatable:

 struct Tuple : IEquatable> { readonly T first; readonly U second; readonly W third; public Tuple(T first, U second, W third) { this.first = first; this.second = second; this.third = third; } public T First { get { return first; } } public U Second { get { return second; } } public W Third { get { return third; } } public override int GetHashCode() { return first.GetHashCode() ^ second.GetHashCode() ^ third.GetHashCode(); } public override bool Equals(object obj) { if (obj == null || GetType() != obj.GetType()) { return false; } return Equals((Tuple)obj); } public bool Equals(Tuple other) { return other.first.Equals(first) && other.second.Equals(second) && other.third.Equals(third); } } 

Entre les approches basées sur les dictionnaires nichés et les dictionnaires nesteds, il est presque toujours préférable d’utiliser le tuple.

Du sharepoint vue de la maintenabilité ,

  • c’est beaucoup plus facile d’implémenter une fonctionnalité qui ressemble à:

     var myDict = new Dictionary, ssortingng>(); 

    que

     var myDict = new Dictionary>>(); 

    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 le code d’un lot significatif dans le second cas (dictionnaire nested) car vous devez append d’autres dictionnaires nesteds et les vérifications ultérieures.

Du sharepoint vue de la performance , la meilleure conclusion à tirer est de la mesurer vous-même. Mais il y a quelques limites théoriques que vous pouvez considérer au préalable:

  • Dans le cas du dictionnaire nested, disposer d’un dictionnaire supplémentaire pour chaque clé (externe et interne) entraînera une surcharge de mémoire (plus que ce que créerait un tuple).

  • Dans le cas du dictionnaire nested, chaque action de base, telle que l’ajout, la mise à jour, la recherche, la suppression, etc. doit être effectuée dans deux dictionnaires. Maintenant, il existe un cas où l’approche par dictionnaire nested 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. En présence de données, il devrait être plus lent car les recherches doivent être effectuées deux fois (ou trois fois en fonction de l’imbrication).

  • En ce qui concerne l’approche tuple, les tuples .NET ne sont pas les plus performants quand ils sont destinés à être utilisés comme clés dans des ensembles, car son implémentation Equals et GetHashCode entraîne la boxe pour les types de valeur .

J’irais avec le dictionnaire basé sur tuple, mais si je veux plus de performance, j’utiliserais mon propre tuple avec une meilleure implémentation.


En passant, peu de produits cosmétiques peuvent rendre le dictionnaire plus cool:

  1. Les appels de type indexeur peuvent être beaucoup plus propres et intuitifs. Par exemple,

     ssortingng foo = dict[a, b, c]; //lookup dict[a, b, c] = ""; //update/insertion 

    Donc, exposez les indexeurs nécessaires dans votre classe de dictionnaire pour gérer en interne les insertions et les recherches.

  2. En outre, implémentez une interface IEnumerable appropriée et fournissez une Add(TypeA, TypeB, TypeC, ssortingng) qui vous donnerait une syntaxe d’initialisation de collection, telle que:

     new MultiKeyDictionary { { a, b, c, null }, ... }; 

Le bon, propre, rapide, facile et lisible est:

Il suffit de créer une classe de clé simple dérivée d’un tuple .

ajoutez quelque chose de similaire comme ceci:

 public sealed class myKey : Tuple { public myKey(TypeA dataA, TypeB dataB, TypeC dataC) : base (dataA, dataB, dataC) { } public TypeA DataA { get { return Item1; } } public TypeB DataB { get { return Item2; } } public TypeC DataC { get { return Item3; } } } 

Vous pouvez donc l’utiliser avec le dictionnaire:

 var myDictinaryData = new Dictionary() { {new myKey(1, 2, 3), "data123"}, {new myKey(4, 5, 6), "data456"}, {new myKey(7, 8, 9), "data789"} }; 
  • Vous pouvez également l’utiliser dans les contrats
  • comme clé pour rejoindre ou grouper dans linq
  • en allant de cette façon, vous n’avez jamais eu à sortinger l’ordre de Item1, Item2, Item3 …
  • vous n’avez pas besoin de vous rappeler ou de regarder pour coder pour comprendre où aller pour obtenir quelque chose
  • pas besoin de remplacer IStructuralEquatable, IStructuralComparable, IComparable, ITuple ils tous déjà ici

Si, pour une raison quelconque, vous voulez vraiment éviter de créer votre propre classe Tuple, ou d’utiliser des fonctions intégrées à .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 ne prenant pas plus de 64 bits, vous pouvez les combiner en un ulong .

Dans le pire des cas, vous pouvez toujours utiliser une chaîne, à condition de vous assurer que les trois composants sont délimités par un caractère ou une séquence qui ne figure pas dans les composants de la clé, par exemple avec trois chiffres.

 ssortingng.Format("{0}#{1}#{2}", key1, key2, key3) 

Il y a évidemment une surcharge de composition dans cette approche, mais en fonction de ce que vous utilisez, cela peut être insignifiant.

Je surchargerais votre Tuple avec un GetHashCode correct, et je l’utiliserais simplement comme clé.

Tant que vous surchargez les méthodes appropriées, vous devriez voir des performances décentes.

Voici le tuple .NET à titre de référence:

 [Serializable] public class Tuple : IStructuralEquatable, IStructuralComparable, IComparable, ITuple { private readonly T1 m_Item1; private readonly T2 m_Item2; private readonly T3 m_Item3; public T1 Item1 { get { return m_Item1; } } public T2 Item2 { get { return m_Item2; } } public T3 Item3 { get { return m_Item3; } } public Tuple(T1 item1, T2 item2, T3 item3) { m_Item1 = item1; m_Item2 = item2; m_Item3 = item3; } public override Boolean Equals(Object obj) { return ((IStructuralEquatable) this).Equals(obj, EqualityComparer.Default);; } Boolean IStructuralEquatable.Equals(Object other, IEqualityComparer comparer) { if (other == null) return false; Tuple objTuple = other as Tuple; if (objTuple == null) { return false; } return comparer.Equals(m_Item1, objTuple.m_Item1) && comparer.Equals(m_Item2, objTuple.m_Item2) && comparer.Equals(m_Item3, objTuple.m_Item3); } Int32 IComparable.CompareTo(Object obj) { return ((IStructuralComparable) this).CompareTo(obj, Comparer.Default); } Int32 IStructuralComparable.CompareTo(Object other, IComparer comparer) { if (other == null) return 1; Tuple objTuple = other as Tuple; if (objTuple == null) { throw new ArgumentException(Environment.GetResourceSsortingng("ArgumentException_TupleIncorrectType", this.GetType().ToSsortingng()), "other"); } int c = 0; c = comparer.Compare(m_Item1, objTuple.m_Item1); if (c != 0) return c; c = comparer.Compare(m_Item2, objTuple.m_Item2); if (c != 0) return c; return comparer.Compare(m_Item3, objTuple.m_Item3); } public override int GetHashCode() { return ((IStructuralEquatable) this).GetHashCode(EqualityComparer.Default); } Int32 IStructuralEquatable.GetHashCode(IEqualityComparer comparer) { return Tuple.CombineHashCodes(comparer.GetHashCode(m_Item1), comparer.GetHashCode(m_Item2), comparer.GetHashCode(m_Item3)); } Int32 ITuple.GetHashCode(IEqualityComparer comparer) { return ((IStructuralEquatable) this).GetHashCode(comparer); } public override ssortingng ToSsortingng() { SsortingngBuilder sb = new SsortingngBuilder(); sb.Append("("); return ((ITuple)this).ToSsortingng(sb); } ssortingng ITuple.ToSsortingng(SsortingngBuilder sb) { sb.Append(m_Item1); sb.Append(", "); sb.Append(m_Item2); sb.Append(", "); sb.Append(m_Item3); sb.Append(")"); return sb.ToSsortingng(); } int ITuple.Size { get { return 3; } } } 

Si votre code consommateur peut se débrouiller avec une interface IDictionary <>, au lieu de Dictionary, mon instinct aurait été d’utiliser un SortedDictionary <> avec un comparateur de tableau personnalisé, à savoir:

 class ArrayComparer : IComparer> where T : IComparable { public int Compare(IList x, IList y) { int compare = 0; for (int n = 0; n < x.Count && n < y.Count; ++n) { compare = x[n].CompareTo(y[n]); } return compare; } } 

Et créer ainsi (en utilisant int [] juste pour un exemple concret):

 var dictionary = new SortedDictionary(new ArrayComparer());