Hashtable с многомерным ключом в C # - PullRequest
60 голосов
/ 27 марта 2009

Я в основном ищу способ доступа к значению хеш-таблицы, используя двумерный типизированный ключ в c #.

В конце концов я смогу сделать что-то вроде этого

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

Это то, что я пытался ... не сработало

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(); 

Ответы [ 16 ]

68 голосов
/ 27 марта 2009

Я думаю, что лучшим подходом является инкапсуляция множества полей вашего многомерного ключа в класс / структуру. Например

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

Теперь вы можете создавать и использовать обычный HashTable и использовать эту оболочку в качестве ключа.

23 голосов
/ 27 марта 2009

Как насчет использования обычного словаря с некоторой структурой кортежей в качестве ключа?

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...
    }
}
18 голосов
/ 05 апреля 2013

На всякий случай, если кто-то здесь недавно, пример того, как сделать это быстрым и грязным способом в .Net 4.0, как описано одним из комментаторов.

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."
  }
}
15 голосов
/ 19 октября 2017

Теперь вы можете сделать это в C # 7.0 с новыми кортежами:

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

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

// Get
int a = test[(1, false)];
15 голосов
/ 27 марта 2009

Я думаю, что это может быть ближе к тому, что вы ищете ...

var data = new Dictionary<int, Dictionary<bool, int>>();
5 голосов
/ 27 марта 2009

Я бы предложил небольшой вариант решения Яхимко, который позволит вам избежать создания класса для пар ключей. Вместо этого оберните частный словарь словарей так:

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;
        }
    }
}
4 голосов
/ 27 марта 2009

вам нужен класс ключей для диктонары, которая правильно реализует gethashcode. И вы можете продлить диктонарность, чтобы позволить вам доступ к нему дружественным путем.

класс пары ключей

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);
    }
}

продлить диктонары <>

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;
        }
    }
}

и вы используете это так

        KeyPairDictonary<int, bool, string> dict = 
            new KeyPairDictonary<int, bool, string>();
        dict[1, false] = "test";
        string test = dict[1, false];
1 голос
/ 29 июля 2010

По сути вам нужно использовать встроенную хеш-таблицу. Если вы думаете о своем вопрос, хеш-таблица с двумя ключами - это функция с двумя независимыми переменные, и f(x,y) является двумерным по определению.

Но вы хотите использовать его как одну хеш-таблицу, а не как встроенные хэши. Итак, вам нужно создать объект, который обтекает эту встроенную идею хеш-таблицы и работает как единый хеш.

Пару коряги:

  • Вы хотите перебрать его, поэтому вам нужно перезаписать метод GetEnumerator(). И вам нужен собственный итератор, который будет правильно выполнять итерацию в двух измерениях.
  • Вам нужно больше проверять, чтобы убедиться, что нет дубликатов.

Я включил свой код для этого:

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 голос
/ 27 марта 2009

Я бы посоветовал вам создать небольшой пользовательский класс, представляющий свойства bool и int, переопределить его методы GetHashCode и Equals, а затем использовать его в качестве ключа.

0 голосов
/ 09 августа 2018

Я думаю, что самый простой способ сделать это сейчас - это использовать Tupple.Create и 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

или используйте новый синтаксис кортежа c # 7 для создания ключей кортежа:

var k = (item1: "value1", item2: 123);
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...