69

我基本上是在寻找一种在 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(); 
4

16 回答 16

73

我认为更好的方法是将多维键的许多字段封装到一个类/结构中。例如

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 并将此包装器用作 Key。

于 2009-03-27T14:25:15.347 回答
50

您现在可以在 C# 7.0 中使用新的元组执行此操作:

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

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

// Get
int a = test[(1, false)];
于 2017-10-19T12:11:00.750 回答
23

如何使用具有某种元组结构的常规字典作为键?

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...
    }
}
于 2009-03-27T14:25:21.540 回答
19

我认为这可能更接近您正在寻找的...

var data = new Dictionary<int, Dictionary<bool, int>>();
于 2009-03-27T14:24:01.903 回答
19

以防万一最近有人在这里,如其中一位评论者所述,如何在 .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."
  }
}
于 2013-04-05T05:24:58.623 回答
5

我建议对 jachymko 的解决方案稍作改动,这样您就可以避免为密钥对创建一个类。相反,包装一个私有字典,如下所示:

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;
        }
    }
}
于 2009-03-27T15:43:05.817 回答
4

您需要一个正确Dictonary实现的关键类。GetHashCode并且您可以扩展Dictonary以让您以友好的方式访问它。

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

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

你可以像这样使用它:

KeyPairDictonary<int, bool, string> dict = 
    new KeyPairDictonary<int, bool, string>();

dict[1, false] = "test";
string test = dict[1, false];
于 2009-03-27T14:53:39.197 回答
1

我建议您创建一个小的自定义类来公开 bool 和 int 属性,并覆盖其 GetHashCode 和 Equals 方法,然后将其用作键。

于 2009-03-27T14:24:36.363 回答
1

本质上,您需要使用嵌入式哈希表。如果您考虑一下您的问题,则具有两个键的哈希表是具有两个自变量的函数,并且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
        }

    }
}
于 2010-07-29T16:50:06.870 回答
0

您可能能够“双重嵌套”您的哈希表 - 换句话说,您的主 Dictionary 是 type Dictionary<int, Dictionary<bool, my_return_type>>

这实现了您能够在第一个代码片段中使用双括号表示法的目标。

当然,管理方面有点棘手。每次添加条目时,需要测试主字典是否包含主键字典,如果没有则添加新字典,然后将辅助键和值添加到内部字典中。

于 2009-03-27T14:25:02.167 回答
0

你能用一个Dictionary<KeyValuePair<int,bool>,int>吗?

于 2009-03-27T14:25:38.733 回答
0

将您的二维密钥包装在一个单独的文件中type并将该类型用作密钥。还要考虑覆盖GetHashCode()Equals()方法。最好使用Dictionary<>而不是HashTable因为显然你可以使用它。

于 2009-03-27T14:28:44.700 回答
0

一种快速而肮脏的方法是从两条信息中创建一个复合键,例如

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

为了更好地封装这一点,您可以包装字典:

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

为了使它更健壮,将复合键封装为一种类型,例如包含两个字段的类,确保您正确覆盖 Equals() 和 GetHashCode() 方法。

于 2009-03-27T14:30:39.470 回答
0

看,这段代码工作得很好:

    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
    }

现在我们只需要一个包装器来避免手动执行此操作。Services.Add("array1", new Hashtable());

于 2010-03-13T18:12:25.517 回答
0

我认为现在最简单的方法是使用 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);
于 2018-08-09T18:00:03.913 回答
0

这是我的嵌套字典实现:

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);
}
于 2019-07-18T14:32:28.243 回答