web-dev-qa-db-fra.com

Hashtable avec clé multidimensionnelle en C #

Je cherche essentiellement un moyen d'accéder à une valeur de table de hachage en utilisant une clé typée à deux dimensions en c #.

Finalement, je serais capable de faire quelque chose comme ça 

HashTable[1][false] = 5;
int a = HashTable[1][false];
//a = 5

C'est ce que j'ai essayé ... n'a pas fonctionné

Hashtable test = new Hashtable();
test.Add(new Dictionary<int, bool>() { { 1, true } }, 555);
Dictionary<int, bool> temp = new Dictionary<int, bool>() {{1, true}};
string testz = test[temp].ToString(); 
58
Scott Schulthess

Je pense qu'une meilleure approche consiste à encapsuler les nombreux champs de votre clé multidimensionnelle dans une classe/structure. Par exemple

struct Key {
  public readonly int Dimension1;
  public readonly bool Dimension2;
  public Key(int p1, bool p2) {
    Dimension1 = p1;
    Dimension2 = p2;
  }
  // Equals and GetHashCode ommitted
}

Vous pouvez maintenant créer et utiliser un HashTable normal et utiliser ce wrapper comme clé.

67
JaredPar

Que diriez-vous d'utiliser un dictionnaire classique avec une sorte de structure de tuple comme clé?

public class TwoKeyDictionary<K1,K2,V>
{
    private readonly Dictionary<Pair<K1,K2>, V> _dict;

    public V this[K1 k1, K2 k2]
    {
        get { return _dict[new Pair(k1,k2)]; }
    }

    private struct Pair
    {
        public K1 First;
        public K2 Second;

        public override Int32 GetHashCode()
        {
            return First.GetHashCode() ^ Second.GetHashCode();
        }

        // ... Equals, ctor, etc...
    }
}
23
user83286

Juste au cas où quelqu'un serait ici récemment, voici un exemple de la façon de procéder. Net 4.0, comme le décrit l'un des commentateurs, est rapide et compliqué.

class Program
{
  static void Main(string[] args)
  {
     var twoDic = new Dictionary<Tuple<int, bool>, String>();
     twoDic.Add(new Tuple<int, bool>(3, true), "3 and true." );
     twoDic.Add(new Tuple<int, bool>(4, true), "4 and true." );
     twoDic.Add(new Tuple<int, bool>(3, false), "3 and false.");

     // Will throw exception. Item with the same key already exists.
     // twoDic.Add(new Tuple<int, bool>(3, true), "3 and true." );

     Console.WriteLine(twoDic[new Tuple<int, bool>(3,false)]);
     Console.WriteLine(twoDic[new Tuple<int, bool>(4,true)]);
     // Outputs "3 and false." and "4 and true."
  }
}
16
Parrhesia Joe

Je pense que cela pourrait être plus proche de ce que vous recherchez ...

var data = new Dictionary<int, Dictionary<bool, int>>();
15
Jason Punyon

Vous pouvez le faire maintenant dans C # 7.0 avec les nouveaux tuples:

// Declare
var test = new Dictionary<(int, bool), int>();

// Add
test.Add((1, false), 5);

// Get
int a = test[(1, false)];
13
Hüseyin Yağlı

Je suggérerais une légère variation de la solution de jachymko qui vous permettra d'éviter de créer une classe pour les paires de clés. Enveloppez plutôt un dictionnaire privé de dictionnaires, comme suit:

public class MultiDictionary<K1, K2, V>
{
    private Dictionary<K1, Dictionary<K2, V>> dict = 
        new Dictionary<K1, Dictionary<K2, V>>();

    public V this[K1 key1, K2 key2]
    {
        get
        {
            return dict[key1][key2];
        }

        set
        {
            if (!dict.ContainsKey(key1))
            {
                dict[key1] = new Dictionary<K2, V>();
            }
            dict[key1][key2] = value;
        }
    }
}
5
kvb

vous avez besoin d'une classe de clés pour le dictonaire qui implémente gethashcode correctement ..__ Et vous pouvez étendre le dictonary pour vous permettre d'y accéder facilement.

la classe de keypair

public class KeyPair<Tkey1, Tkey2>
{
    public KeyPair(Tkey1 key1, Tkey2 key2)
    {
        Key1 = key1;
        Key2 = key2;
    }

    public Tkey1 Key1 { get; set; }
    public Tkey2 Key2 { get; set; }

    public override int GetHashCode()
    {
        return Key1.GetHashCode() ^ Key2.GetHashCode();
    }
    public override bool Equals(object obj)
    {
        KeyPair<Tkey1, Tkey2> o = obj as KeyPair<Tkey1, Tkey2>;
        if (o == null)
            return false;
        else
            return Key1.Equals(o.Key1) && Key2.Equals(o.Key2);
    }
}

étendre le Dictonary <>

public class KeyPairDictonary<Tkey1, Tkey2, Tvalue> 
    : Dictionary<KeyPair<Tkey1, Tkey2>, Tvalue>
{
    public Tvalue this[Tkey1 key1, Tkey2 key2]
    {
        get
        {
            return this[new KeyPair<Tkey1, Tkey2>(key1, key2)];
        }
        set
        {
            this[new KeyPair<Tkey1, Tkey2>(key1, key2)] = value;
        }
    }
}

et vous l'utilisez comme ça

        KeyPairDictonary<int, bool, string> dict = 
            new KeyPairDictonary<int, bool, string>();
        dict[1, false] = "test";
        string test = dict[1, false];
4
AndreasN

Je vous suggérerais de créer une petite classe personnalisée exposant les propriétés bool et int, de remplacer ses méthodes GetHashCode et Equals, puis de l'utiliser comme clé.

1
David M

Essentiellement, vous devez utiliser une table de hachage intégrée. Si vous pensez à votre question , Une table de hachage avec deux clés est une fonction avec deux variables Indépendantes, et f(x,y) est bidimensionnel par définition. 

Mais vous voulez l'utiliser comme s'il s'agissait d'une table de hachage, et non de tables de hachage intégrées. Vous devez donc créer un objet qui englobe cette idée de hashtable intégrée et fonctionne comme un hachage unique. 

Quelques accrocs: 

  • Vous souhaitez effectuer une itération dessus, vous devez donc écraser la méthode GetEnumerator(). Et vous avez besoin de votre propre Iterator qui itérera correctement en 2 dimensions.
  • Vous devez vérifier davantage pour vous assurer qu'il n'y a pas de doublons.

J'ai inclus mon code pour le faire:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Collections;
using System.Windows.Forms;

namespace YourProjectNameHere
{
    public class Hashtable2D
    {
        /// <summary>
        /// This is a hashtable of hashtables
        /// The X dim is the root key, and the y is the internal hashes key
        /// </summary>
        /// 
        private Hashtable root = new Hashtable();
        public bool overwriteDuplicates = false;
        public bool alertOnDuplicates = true;

        public void Add(object key_x, object key_y, object toStore)
        {
            if(root[key_x]!=null)//If key_x has already been entered 
            {
                Hashtable tempHT = (Hashtable)root[key_x];//IF the hash table does not exist then focus will skip to the catch statement
                if (tempHT[key_y] == null)  tempHT.Add(key_y, toStore);
                else handleDuplicate(tempHT, key_y, toStore);
            }else{//Making a new hashtable 
                Hashtable tempHT = new Hashtable();
                tempHT.Add(key_y, toStore);
                root.Add(key_x, tempHT);
            }

        }

        public void Remove(object key_x, object key_y)
        {
            try{
                ((Hashtable)root[key_x]).Remove(key_y);
            }catch(Exception e){
                MessageBox.Show("That item does not exist");
            }

        }

        public void handleDuplicate (Hashtable tempHT, object key_y, object toStore)
        {
            if (alertOnDuplicates) MessageBox.Show("This Item already Exists in the collection");

            if (overwriteDuplicates)
            {
                tempHT.Remove(key_y);
                tempHT.Add(key_y,toStore);
            }
        }

        public object getItem(object key_x, object key_y)
        {
            Hashtable tempHT = (Hashtable)root[key_x];
            return tempHT[key_y];
        }

        public ClassEnumerator GetEnumerator()
        {
            return new ClassEnumerator(root);
        }

        public class ClassEnumerator : IEnumerator
        {
            private Hashtable ht;
            private IEnumerator iEnumRoot;
            private Hashtable innerHt;
            private IEnumerator iEnumInner;

            public ClassEnumerator(Hashtable _ht)
            {
                ht = _ht;
                iEnumRoot = ht.GetEnumerator();

                iEnumRoot.MoveNext();//THIS ASSUMES THAT THERE IS AT LEAST ONE ITEM

                innerHt = (Hashtable)((DictionaryEntry)iEnumRoot.Current).Value;
                iEnumInner = innerHt.GetEnumerator();
            }

            #region IEnumerator Members

            public void Reset()
            {
                iEnumRoot = ht.GetEnumerator();
            }

            public object Current
            {
                get
                {
                    return iEnumInner.Current; 
                }
            }

            public bool MoveNext()
            {
                if(!iEnumInner.MoveNext())
                {
                    if (!iEnumRoot.MoveNext()) return false;
                    innerHt = (Hashtable)((DictionaryEntry)iEnumRoot.Current).Value;
                    iEnumInner = innerHt.GetEnumerator();
                    iEnumInner.MoveNext();
                }
                return true;
            }

            #endregion
        }

    }
}
1
Jim

Voici l'implémentation de mon dictionnaire imbriqué:

public class TwoKeysDictionary<K1, K2, T>:
        Dictionary<K1, Dictionary<K2, T>>
{
    public T this[K1 key1, K2 key2]
    {
        get => base.ContainsKey(key1) && base[key1].ContainsKey(key2) ? base[key1][key2] : default;
        set
        {
            if (ContainsKey(key1) && base[key1].ContainsKey(key2))
                base[key1][key2] = value;
            else
                Add(key1, key2, value);
        }
    }

    public void Add(K1 key1, K2 key2, T value)
    {
        if (ContainsKey(key1))
        {
            if (base[key1].ContainsKey(key2))
                throw new Exception("Couple " + key1 + "/" + key2 + " already exists!");
            base[key1].Add(key2, value);
        }
        else
            Add(key1, new Dictionary<K2, T>() { { key2, value } });
    }

    public bool ContainsKey(K1 key1, K2 key2) => ContainsKey(key1) && base[key1].ContainsKey(key2);
}
0
tedebus

Vous pourrez peut-être "imbriquer" vos hashtables - autrement dit, votre dictionnaire principal est de type Dictionary<int, Dictionary<bool, my_return_type>>.

Cela vous permet d’atteindre l’objectif de pouvoir utiliser la notation à double crochet dans votre premier extrait de code.

Bien sûr, la gestion est un peu plus délicate. Chaque fois que vous ajoutez une entrée, vous devez vérifier si le dictionnaire principal contient un dictionnaire pour la clé primaire et ajouter un nouveau dictionnaire si ce n'est pas le cas, puis ajoutez la clé secondaire et la valeur au dictionnaire interne.

0
Mike

Pourriez-vous utiliser un Dictionary<KeyValuePair<int,bool>,int>?

0
Andrew Hare

Enveloppez votre clé bidimensionnelle dans une type séparée et utilisez ce type comme clé. Pensez également à remplacer les méthodes GetHashCode() et Equals(). Utilisez de préférence Dictionary<> au lieu de HashTable puisque vous pouvez apparemment utiliser cela.

0
bruno conde

Voici un exemple , vous pouvez utiliser une Hashtable normale à la place de celle que j'ai utilisée.

0
leppie

Une manière rapide et sale serait de créer une clé composite à partir des deux informations, par ex.

IDictionary<string, int> values = new Dictionary<string, int>();
int i = ...;
bool b = ...;
string key = string.Concat(i, '\0', b);
values[key] = 555;

Pour mieux comprendre cela, vous pouvez envelopper le dictionnaire:

public class MyDict
{
    private readonly IDictionary<string, int> values = new Dictionary<string, int>();

    public int this[int i, bool b]
    {
        get
        {
            string key = BuildKey(i, b);
            return values[key];
        }

        set
        {
            string key = BuildKey(i, b);
            values[key] = value;
        }
    }

    private static string BuildKey(int i, bool b)
    {
        return string.Concat(i, '\0', b);
    }
}

Pour rendre cela plus robuste, encapsulez la clé composite en tant que type, par ex. une classe qui contient les deux champs, en veillant à redéfinir correctement les méthodes Equals () et GetHashCode ().

0
Paul Ruane

Je pense que le moyen le plus simple de le faire maintenant est d'utiliser Tuple.Create et ValueTuple.Create:

> var k1 =  Tuple.Create("test", int.MinValue, DateTime.MinValue, double.MinValue);
> var k2 = Tuple.Create("test", int.MinValue, DateTime.MinValue, double.MinValue);
> var dict = new Dictionary<object, object>();
> dict.Add(k1, "item");
> dict.Add(k2, "item");
An item with the same key has already been added....
> dict[k1] == dict[k2]
true

ou utilisez la nouvelle syntaxe c # 7 Tuple pour créer des clés Tuple:

var k = (item1: "value1", item2: 123);
0
SalientBrain

Regardez, ce code fonctionne très bien:

    public Form1()
    {
            InitializeComponent();
    }

    private void Form1_Load(object sender, EventArgs e)
    {

        this.Services = new Dictionary<object, Hashtable>();
        this.Services.Add("array1", new Hashtable());

        this.Services["array1"]["qwe"] = "123";
        this.Services["array1"][22] = 223;

        object zz = null;
        zz = this.Services["array1"]["qwe"];
        MessageBox.Show(zz.ToString()); // shows qwe

        zz = this.Services["array1"][22];
        MessageBox.Show(zz.ToString()); // shows 22
    }

Maintenant, nous avons juste besoin d’un wrapper pour éviter de le faire manuellement.Services.Add ("array1", new Hashtable ());

0
user293071