在C#中使用多维键的散列表
我基本上正在寻找一种方法来访问使用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();
我认为更好的方法是将多维密钥的许多字段封装到类/结构中。 例如
struct Key { public readonly int Dimension1; public readonly bool Dimension2; public Key(int p1, bool p2) { Dimension1 = p1; Dimension2 = p2; } // Equals and GetHashCode ommitted }
现在你可以创build和使用普通的HashTable,并将这个包装器用作Key。
如何使用以某种Tuple结构作为关键字的常规字典?
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... } }
我想这可能更接近你要找的东西
var data = new Dictionary<int, Dictionary<bool, int>>();
以防万一谁最近在这里,一个如何做到这一点在.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." } }
我build议在jachymko的解决scheme稍微变化,这将允许您避免创build一个密钥对类。 相反,包装一个字典的私人字典,如下所示:
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; } } }
您需要正确实现gethashcode的dictonary的关键类。 而且您可以扩展dictonary让您以友好的方式访问它。
密钥对类
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];
我build议你创build一个暴露bool和int属性的小自定义类,并覆盖它的GetHashCode和Equals方法,然后使用它作为键。
基本上你需要使用一个embedded式哈希表。 如果你考虑一下你的问题,一个有两个键的散列表是一个带有两个独立variables的函数, f(x,y)
是定义的二维。
但是,你想使用它像一个哈希表,而不是embedded哈希。 所以你需要做的是创build一个对象,围绕该embedded式哈希表的想法,并像一个单一的哈希操作。
一些障碍:
- 你想迭代它,所以你需要覆盖
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 } } }
你现在可以在C#7.0中用新的元组来完成这个工作:
// Declare var test = new Dictionary<(int, bool), int>(); // Add test.Add((1, false), 5); // Get int a = test[(1, false)];
你可能能够“双重”你的哈希表 – 换句话说,你的主要字典是typesDictionary<int, Dictionary<bool, my_return_type>>
。
这就完成了你能够在你的第一个代码片段中使用双括号符号的目标。
当然,pipe理方面有点棘手。 每次添加条目时,都需要testing主字典是否包含主键字典,如果不是,则添加新字典,然后将副键和值添加到内部字典中。
你可以使用Dictionary<KeyValuePair<int,bool>,int>
?
将二维密钥包装在一个单独的type
并将该types用作密钥。 还要考虑重写GetHashCode()
和Equals()
方法。 最好使用Dictionary<>
来代替HashTable
因为显然你可以使用它。
这里是一个例子 ,你可以使用一个普通的Hashtable而不是我使用的。
一个快速和肮脏的方法是从这两个信息创build一个复合键,例如
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); } }
为了使其更健壮,将组合键封装为一个types,例如包含两个字段的类,确保正确覆盖Equals()和GetHashCode()方法。
看,这段代码工作得很好:
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 }
现在我们只需要一个包装来避免手动执行this.Services.Add(“array1”,new Hashtable());