C # 中的多键字典?

我知道 BCL 里没有,但是谁能告诉我一个好的开源软件呢?

我说的多指的是2个键。 ; -)

216081 次浏览

目前,我只是简单地将这些键连接到一个字符串中,作为一种解决方案。当然,这对非字符串键不起作用。我也想知道答案。

我使用 Tuple作为 Dictionary中的键。

public class Tuple<T1, T2> {
public T1 Item1 { get; private set; }
public T2 Item2 { get; private set; }


// implementation details
}

一定要覆盖 EqualsGetHashCode,并适当地定义 operator!=operator==。您可以扩展 Tuple以根据需要保存更多的项。.NET 4.0将包括一个内置的 Tuple

在此之前,您还可以使用

 Dictionary<key1, Dictionary<key2, TypeObject>>

或者,创建一个自定义集合类来表示..。

 public class TwoKeyDictionary<K1, K2, T>:
Dictionary<K1, Dictionary<K2, T>> { }

或者,用三把钥匙。

public class ThreeKeyDictionary<K1, K2, K3, T> :
Dictionary<K1, Dictionary<K2, Dictionary<K3, T>>> { }

看看 Wintelligence 的 电力收集(CodePlex 下载) ,我认为他们的多词典就是这样的。

这是一个字典的字典,所以你有两个键来访问每个对象,键的主字典得到你所需的子字典,然后第二个键的子字典得到你所需的项目。你是这个意思吗?

我想你需要一个类似 Tuple2的类。确保它是 GetHashCode ()和 Equals ()基于两个包含的元素。

参见 C # 中的元组

New Dictionary < KeyValuePair < object,object > ,object > 新字典 < KeyValuePair < object,object > 
有什么问题吗?

我也像 Jason 的回答一样使用了 tuple,但是,我建议您将 tuple 定义为 struct:

public struct Tuple<T1, T2> {
public readonly T1 Item1;
public readonly T2 Item2;
public Tuple(T1 item1, T2 item2) { Item1 = item1; Item2 = item2;}
}


public static class Tuple { // for type-inference goodness.
public static Tuple<T1,T2> Create<T1,T2>(T1 item1, T2 item2) {
return new Tuple<T1,T2>(item1, item2);
}
}

您可以免费获得不可变性,.GetHashcode.Equals,这(在您等待 C # 4.0时)非常好,非常简单..。

但是有一个 警告: 默认的 GetHashcode实现(有时候) 只考虑第一个领域,所以要确保第一个字段是最有区别的,或者自己实现 GetHashcode(例如使用 价值工具中的 FieldwiseHasher.Hash(this)) ,否则可能会遇到可伸缩性问题。

而且,您可以避免使问题复杂化的 null (如果您真的想要 null,您只需要使您的 Tuple<>可以为空)。有点跑题了,我是唯一一个对框架级别缺乏对非空引用的支持感到恼火的人吗?我在一个大型项目中工作,偶尔会有一个 null 出现在它不应该出现的地方——嘿,很快,你就会得到一个 null 引用异常——但是通过堆栈跟踪可以指向引用的第一次使用,而不是实际上错误的代码。

当然,.NET 4.0现在已经很老了; 我们大多数人只能使用.NET 4.0的 tuple。

编辑: 以解决 GetHashCode实现不佳的问题。NET 提供了我编写的结构 价值工具,它还允许您为多字段键使用实名; 这意味着您可能要编写如下内容:

sealed class MyValueObject : ValueObject<MyValueObject> {
public DayOfWeek day;
public string NamedPart;
//properties work fine too
}

... 这有望使人们更容易对具有值语义的数据使用人类可读的名称,至少在 一些未来版本的 C # 使用命名成员实现了正确的元组之前是这样; 有望使用像样的散列码; ——)。

我在谷歌上搜索过这个: http://www.codeproject.com/KB/recipes/multikey-dictionary.aspx。与使用 struct 在普通字典中包含2个键相比,我猜它的主要特性是您可以稍后通过其中一个键进行引用,而不必提供2个键。

你能用 Dictionary<TKey1,Dictionary<TKey2,TValue>>吗?

你甚至可以将其子类化:

public class DualKeyDictionary<TKey1,TKey2,TValue> : Dictionary<TKey1,Dictionary<TKey2,TValue>>

编辑: 现在这是一个重复的答案。它的实用性也是有限的。虽然它确实“工作”并提供编码 dict[key1][key2]的能力,但是还有很多“变通方法”可以让它“只是工作”。

然而: 只是为了好玩,人们还是可以实现 Dictionary,但是在这一点上它有点冗长:

public class DualKeyDictionary<TKey1, TKey2, TValue> : Dictionary<TKey1, Dictionary<TKey2, TValue>> , IDictionary< object[], TValue >
{
#region IDictionary<object[],TValue> Members


void IDictionary<object[], TValue>.Add( object[] key, TValue value )
{
if ( key == null || key.Length != 2 )
throw new ArgumentException( "Invalid Key" );


TKey1 key1 = key[0] as TKey1;
TKey2 key2 = key[1] as TKey2;


if ( !ContainsKey( key1 ) )
Add( key1, new Dictionary<TKey2, TValue>() );


this[key1][key2] = value;
}


bool IDictionary<object[], TValue>.ContainsKey( object[] key )
{
if ( key == null || key.Length != 2 )
throw new ArgumentException( "Invalid Key" );


TKey1 key1 = key[0] as TKey1;
TKey2 key2 = key[1] as TKey2;


if ( !ContainsKey( key1 ) )
return false;


if ( !this[key1].ContainsKey( key2 ) )
return false;


return true;
}

下面是一个充实的成对类示例,它可以用作 Dictionary的键。

public class Pair<T1, T2>
{
public T1 Left { get; private set; }
public T2 Right { get; private set; }


public Pair(T1 t1, T2 t2)
{
Left = t1;
Right = t2;
}


public override bool Equals(object obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
if (obj.GetType() != typeof(Pair<T1, T2>)) return false;
return Equals((Pair<T1, T2>)obj);
}


public bool Equals(Pair<T1, T2> obj)
{
if (ReferenceEquals(null, obj)) return false;
if (ReferenceEquals(this, obj)) return true;
return Equals(obj.Left, Left) && Equals(obj.Right, Right);
}


public override int GetHashCode()
{
unchecked
{
return (Left.GetHashCode() * 397) ^ Right.GetHashCode();
}
}
}

我经常使用这个,因为它很短,而且提供了我需要的句法糖..。

public class MultiKeyDictionary<T1, T2, T3> : Dictionary<T1, Dictionary<T2, T3>>
{
new public Dictionary<T2, T3> this[T1 key]
{
get
{
if (!ContainsKey(key))
Add(key, new Dictionary<T2, T3>());


Dictionary<T2, T3> returnObj;
TryGetValue(key, out returnObj);


return returnObj;
}
}
}

使用方法:

dict[cat][fish] = 9000;

“ Cat”密钥也不存在的地方。

我写了这篇文章,并成功地运用了它。

public class MultiKeyDictionary<K1, K2, V> : Dictionary<K1, Dictionary<K2, V>>  {


public V this[K1 key1, K2 key2] {
get {
if (!ContainsKey(key1) || !this[key1].ContainsKey(key2))
throw new ArgumentOutOfRangeException();
return base[key1][key2];
}
set {
if (!ContainsKey(key1))
this[key1] = new Dictionary<K2, V>();
this[key1][key2] = value;
}
}


public void Add(K1 key1, K2 key2, V value) {
if (!ContainsKey(key1))
this[key1] = new Dictionary<K2, V>();
this[key1][key2] = value;
}


public bool ContainsKey(K1 key1, K2 key2) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2);
}


public new IEnumerable<V> Values {
get {
return from baseDict in base.Values
from baseKey in baseDict.Keys
select baseDict[baseKey];
}
}


}




public class MultiKeyDictionary<K1, K2, K3, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, V>> {
public V this[K1 key1, K2 key2, K3 key3] {
get {
return ContainsKey(key1) ? this[key1][key2, key3] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, V>();
this[key1][key2, key3] = value;
}
}


public bool ContainsKey(K1 key1, K2 key2, K3 key3) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, V>();
this[key1][key2, key3, key4] = value;
}
}


public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, V>();
this[key1][key2, key3, key4, key5] = value;
}
}


public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, V>();
this[key1][key2, key3, key4, key5, key6] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, V>();
this[key1][key2, key3, key4, key5, key6, key7] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8, key9] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10);
}
}


public class MultiKeyDictionary<K1, K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V> : Dictionary<K1, MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>> {
public V this[K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11] {
get {
return ContainsKey(key1) ? this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] : default(V);
}
set {
if (!ContainsKey(key1))
this[key1] = new MultiKeyDictionary<K2, K3, K4, K5, K6, K7, K8, K9, K10, K11, V>();
this[key1][key2, key3, key4, key5, key6, key7, key8, key9, key10, key11] = value;
}
}
public bool ContainsKey(K1 key1, K2 key2, K3 key3, K4 key4, K5 key5, K6 key6, K7 key7, K8 key8, K9 key9, K10 key10, K11 key11) {
return base.ContainsKey(key1) && this[key1].ContainsKey(key2, key3, key4, key5, key6, key7, key8, key9, key10, key11);
}
}

这是我的实现,我想隐藏 Tuple 概念的实现。

  public class TwoKeyDictionary<TKey1, TKey2, TValue> : Dictionary<TwoKey<TKey1, TKey2>, TValue>
{
public static TwoKey<TKey1, TKey2> Key(TKey1 key1, TKey2 key2)
{
return new TwoKey<TKey1, TKey2>(key1, key2);
}


public TValue this[TKey1 key1, TKey2 key2]
{
get { return this[Key(key1, key2)]; }
set { this[Key(key1, key2)] = value; }
}


public void Add(TKey1 key1, TKey2 key2, TValue value)
{
Add(Key(key1, key2), value);
}


public bool ContainsKey(TKey1 key1, TKey2 key2)
{
return ContainsKey(Key(key1, key2));
}
}


public class TwoKey<TKey1, TKey2> : Tuple<TKey1, TKey2>
{
public TwoKey(TKey1 item1, TKey2 item2) : base(item1, item2) { }


public override string ToString()
{
return string.Format("({0},{1})", Item1, Item2);
}
}

它有助于使用法看起来像字典

item.Add(1, "D", 5.6);


value = item[1, "D"];

如果有人正在寻找一个 ToMultiKeyDictionary()这里是一个实现,应该与大部分的答案在这里(基于 赫尔曼的) :

public static class Extensions_MultiKeyDictionary {


public static MultiKeyDictionary<K1, K2, V> ToMultiKeyDictionary<S, K1, K2, V>(this IEnumerable<S> items, Func<S, K1> key1, Func<S, K2> key2, Func<S, V> value) {
var dict = new MultiKeyDictionary<K1, K2, V>();
foreach (S i in items) {
dict.Add(key1(i), key2(i), value(i));
}
return dict;
}


public static MultiKeyDictionary<K1, K2, K3, V> ToMultiKeyDictionary<S, K1, K2, K3, V>(this IEnumerable<S> items, Func<S, K1> key1, Func<S, K2> key2, Func<S, K3> key3, Func<S, V> value) {
var dict = new MultiKeyDictionary<K1, K2, K3, V>();
foreach (S i in items) {
dict.Add(key1(i), key2(i), key3(i), value(i));
}
return dict;
}
}

这里有很多好的解决方案, 这里我缺少的是一个基于 Tuple类型构建的实现,所以我自己编写了一个。

因为它只是从 Dictionary<Tuple<T1,T2>, T>继承而来,所以总是可以同时使用这两种方式。

var dict = new Dictionary<int, int, Row>();
var row = new Row();
dict.Add(1, 2, row);
dict.Add(Tuple.Create(1, 2, row));
dict.Add(new Tuple<int, int>(1, 2));

这是密码。

public class Dictionary<TKey1,TKey2,TValue> :  Dictionary<Tuple<TKey1, TKey2>, TValue>, IDictionary<Tuple<TKey1, TKey2>, TValue>
{


public TValue this[TKey1 key1, TKey2 key2]
{
get { return base[Tuple.Create(key1, key2)]; }
set { base[Tuple.Create(key1, key2)] = value; }
}


public void Add(TKey1 key1, TKey2 key2, TValue value)
{
base.Add(Tuple.Create(key1, key2), value);
}


public bool ContainsKey(TKey1 key1, TKey2 key2)
{
return base.ContainsKey(Tuple.Create(key1, key2));
}
}

请注意,此实现依赖于 Tuple:

Http://msdn.microsoft.com/en-us/library/dd270346(v=vs.110).aspx

在下列条件下,obj 参数被认为等于当前实例:

  • 它是一个 Tuple 对象。
  • 它的两个组件与当前实例的类型相同。
  • 它的两个组件等于当前实例的组件。相等性由每个组件的默认对象相等比较器确定。

下面是另一个使用 Tuple 类和 Dictionary 的示例。

        // Setup Dictionary
Dictionary<Tuple<string, string>, string> testDictionary = new Dictionary<Tuple<string, string>, string>
{
{new Tuple<string, string>("key1","key2"), "value1"},
{new Tuple<string, string>("key1","key3"), "value2"},
{new Tuple<string, string>("key2","key3"), "value3"}
};
//Query Dictionary
public string FindValue(string stuff1, string stuff2)
{
return testDictionary[Tuple.Create(stuff1, stuff2)];
}