字典,其中键是一对整数

时间:2012-09-24 18:07:44

标签: c# .net collections dictionary

我需要使用Dictionary,其中TKey是一对int。

我想使用KeyValuePair作为我的键类型,我想知道这是否是最好的方式

我也很想知道词典是否会为两个不同的KeyValuePair对象创建单独的条目,这些对象具有相同的整数

例如:

var myDictionary = new Dictionary<KeyValuePair<int,int>, string>();
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem");
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "SecondItem");
// does the dictionary allow this?

6 个答案:

答案 0 :(得分:24)

也许您应该考虑使用Tuple

var myDictionary = new Dictionary<Tuple<int,int>, List<string>>(); 
myDictionary.Add(new Tuple<int,int>(3, 3), "FirstItem"); 
myDictionary.Add(new Tuple<int,int>(5, 5), "SecondItem"); 

根据MSDN documentationTuple对象Equals方法将使用两个Tuple对象的值。这将导致外部字典中每个Tuple一个条目,并允许您存储每个键的值列表。

答案 1 :(得分:4)

对于性能词典,需要一个生成唯一GetHashValue的键。

KeyValuePair是一种值类型,不建议用于密钥。

ValueType.GetHashCode

  

如果调用派生类型的GetHashCode方法,则返回值为   不太可能适合用作哈希表中的键。   另外,如果这些字段中的一个或多个的值发生变化,那么   返回值可能不适合用作哈希表中的键。   在任何一种情况下,考虑编写自己的实现   GetHashCode方法更接近地表示哈希的概念   该类型的代码。

Point也是值值类型,也不建议用于键 元组还会生成大量重复的GetHashCode,并不是一个好的关键。

最佳密钥是生成唯一密钥的密钥。

将UInt16 i和UInt j视为两个键 它们如何组合并生成唯一的哈希? 轻松将它们组合到UInt32中 UInt32本身生成一个完美的哈希。

将两个UInt16打包到UInt32中的算法是

(i * (UInt16.MaxValue + 1)) + j;

更快
(UInt32)i << 16 | j;


myDictionary = new Dictionary<UInt32, string>();

使用完美哈希,词典是O(1) 如果哈希差,则字典变为O(n)。

答案 2 :(得分:2)

只需使用long作为键,然后合并两个int

public class IntIntDict<T> : Dictionary<long, T>
{
    public void Add(int key1, int key2, T value)
    {
        Add((((long)key1) << 32) + key2, value);
    }

    //TODO: Overload other methods
}

更新

C#7引入了新的ValueTuple Structsimplified tuple syntax。这些元组对复合键很方便。您可以声明字典并添加如下条目:

var myDictionary = new Dictionary<(int, int), string>();
myDictionary.Add((3, 3), "FirstItem"); 
myDictionary.Add((5, 5), "SecondItem");

并查找像这样的值

string result = myDictionary[(5, 5)];

if (myDictionary.TryGetValue((5, 7), out string result)) {
    //TODO: use result
}

答案 3 :(得分:1)

更新:根据您对其他响应者的评论,以下代码可以回答您的问题。是的,重复项将生成异常,System.ArgumentException

您列出的代码可以使用,但不会接受重复的KeyValuePairs。如果添加字典中已存在的KeyValuePair,则抛出System.ArgumentException或类似内容。

例如,此代码

using System;
using System.Collections;
using System.Collections.Generic;

namespace test{

    public class App {

        public static void Main(string[] args) {
            var myDictionary = new Dictionary<KeyValuePair<int,int>, string>(); 

            Console.WriteLine("Adding 2 items...");
            myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem"); 
            myDictionary.Add(new KeyValuePair<int,int>(5, 5), "SecondItem"); 
            Console.WriteLine("Dictionary items: {0}", myDictionary.Count);

            Console.WriteLine("Adding 2 duplicate items...");
            myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem"); 
            myDictionary.Add(new KeyValuePair<int,int>(5, 5), "SecondItem"); 
            Console.WriteLine("Dictionary items: {0}", myDictionary.Count);
        }
    }
}

给出以下

Microsoft(R)Visual C#编译器版本4.0.30319.17626 适用于Microsoft(R).NET Framework 4.5 版权所有(C)Microsoft Corporation。保留所有权利。

添加2个项目...... 字典项目:2 添加2个重复的项目......

  

未处理的异常:System.ArgumentException:具有相同的项目   密钥已添加。在   System.Collections.Generic.Dictionary`2.Insert(TKey键,TValue值,   test.App.Main(String [] args)

中的Boolean add)

答案 4 :(得分:1)

  

Dictionary需要一个相等的实现来确定密钥是否相等。您可以使用接受comparer参数的构造函数指定IEqualityComparer<T>泛型接口的实现;如果未指定实现,则使用默认的通用相等比较器EqualityComparer<T>.Default

因此,在您的情况下,因为您未指定IEqualityComparer<T>,将使用默认值。

  

EqualityComparer<T>.Default检查类型T是否实现System.IEquatable<T>接口,如果是,则返回使用该实现的EqualityComparer。否则,它会返回使用T提供的EqualityComparer<T>Object.Equals覆盖的Object.GetHashCode

T是struct KeyValuePair未实现System.IEquatable<T>,因此它使用struct Equal的{​​{1}}和GetHashCode方法。这两种方法使用KeyValuePairKey来检查相等并生成哈希码:

Value

所以,总而言之,在你的示例词典中不允许。

答案 5 :(得分:0)

使用KeyValuePair作为词典的键:

在功能上可以使用KeyValuePair作为字典中的键;但是,概念上可能不是您的应用程序的最佳选择,因为它意味着两个整数之间的键 - 值关系。

相反,迈克建议你应该使用元组作为你的钥匙。

关于第二个问题:

var myDictionary = new Dictionary<KeyValuePair<int,int>, string>();  
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "FirstItem");  
myDictionary.Add(new KeyValuePair<int,int>(3, 3), "SecondItem");  
// does the dictionary allow this?  

字典不允许这样做,字典本身就是键值必须唯一的键值对。如果您希望能够将多个值映射到同一个键,则一个选项是将该值设置为另一个集合:

var myDictionary = new Dictionary<KeyValuePair<int,int>, List<string>>();  

但是你仍然无法像你的例子那样使用myDictionary.Add。 相反,你必须提供额外的功能来确定它们的密钥是否是字典的一部分并采取相应的行动:

public static class DictionaryHelper
{

    public static void Add(this Dictionary<Tuple<int,int>, List<string>> dict,Tuple<int,int> key, string value)
    {
        if(dict.ContainsKey(key))
        {
            dict[key].Add(value);
        }
        else
        {
            dict.Add(key, new List<string>{value});
        }
    } 
}