字典和哈希表的区别

111

希望这个问题的答案(https://dev59.com/ZHVC5IYBdhLWcg3wbQbA)能为您提供一个好的答案。 - TheVillageIdiot
2
看起来这个问题应该被关闭为重复,并将其答案与其中一个重复项合并。 - John Saunders
虽然与Java语言相关,但这个线程值得一读,以了解HashMapHashTable之间的区别。一些差异也适用于C#世界。 - RBT
7个回答

199

Dictionary<TKey,TValue>是一种通用类型,其特点包括:

  • 静态类型(并在编译时进行验证)
  • 无需装箱即可使用

如果您使用的是.NET 2.0或更高版本,则应该优先使用 Dictionary<TKey,TValue>(以及其他通用集合)

一个微妙但重要的区别是,Hashtable支持使用单个writer线程的多个reader线程,而Dictionary则不提供线程安全。如果您需要使用通用字典进行线程安全操作,则必须实现自己的同步或(在.NET 4.0中)使用ConcurrentDictionary<TKey, TValue>


3
字典类的实例方法不是线程安全的,不像哈希表。 - t3mujin

86

让我们举一个例子来解释哈希表和字典之间的区别。

这里有一个实现哈希表的方法:

public void MethodHashTable()
{
    Hashtable objHashTable = new Hashtable();
    objHashTable.Add(1, 100);    // int
    objHashTable.Add(2.99, 200); // float
    objHashTable.Add('A', 300);  // char
    objHashTable.Add("4", 400);  // string

    lblDisplay1.Text = objHashTable[1].ToString();
    lblDisplay2.Text = objHashTable[2.99].ToString();
    lblDisplay3.Text = objHashTable['A'].ToString();
    lblDisplay4.Text = objHashTable["4"].ToString();


    // ----------- Not Possible for HashTable ----------
    //foreach (KeyValuePair<string, int> pair in objHashTable)
    //{
    //    lblDisplay.Text = pair.Value + " " + lblDisplay.Text;
    //}
}

以下内容是关于字典的:

  public void MethodDictionary()
  {
    Dictionary<string, int> dictionary = new Dictionary<string, int>();
    dictionary.Add("cat", 2);
    dictionary.Add("dog", 1);
    dictionary.Add("llama", 0);
    dictionary.Add("iguana", -1);

    //dictionary.Add(1, -2); // Compilation Error

    foreach (KeyValuePair<string, int> pair in dictionary)
    {
        lblDisplay.Text = pair.Value + " " + lblDisplay.Text;
    }
  }

3
作为一条注释,你可以枚举 Hashtable 属性,具体描述见这里:http://msdn.microsoft.com/zh-cn/library/system.collections.hashtable.aspx。你需要使用 DictionaryEntry 作为变量,然后它可以提供键和值对象。 - user677526
1
非常好的答案,此外您还可以使用DictionaryEntry来枚举Hashtable。例如:foreach(DictionaryEntry dnty in objHashTable){lblDisplay.Text= dnty.Value + " " +dnty.Key} - Manoj Weerasooriya
//循环哈希表 foreach (DictionaryEntry entry in objHashTable) { Console.WriteLine("{0}, {1}", entry.Key, entry.Value); } - Mike

24

哈希表和字典之间还有一个重要的区别。如果使用索引器从哈希表中获取值,对于不存在的项,哈希表将成功返回null,而如果您尝试使用在字典中不存在的索引器访问项,字典将抛出错误。


13

字典(Dictionary)是强类型的(因此值类型不需要装箱),哈希表(Hashtable)则不是(所以值类型需要装箱)。在我看来,哈希表比字典有更好的获取值的方式,因为它总是知道该值是一个对象。但是,如果您使用的是.NET 3.5,则很容易编写一个扩展方法来使字典获得类似的行为。

如果您需要每个键有多个值,请查看我的MultiValueDictionary源代码: .NET中的Multimap


2
对于每个键有多个值的情况:在.NET 3.5中,您可能还考虑实现ILookup<TKey,TValue>(这是多映射接口)。不幸的是,默认的具体实现是不可变的,但很容易重新实现(或添加到您的MultiValueDictionary中)。在MiscUtil中有一个简单的示例(EditableLookup<TKey,TValue>)。 - Marc Gravell
确实是个好建议,我已经忘记了那个接口。我查看了BCL中的实现,但它确实是不可变的,因此在日常多值使用中几乎没有用处 ;)。我会添加这个接口。 - Frans Bouma
已完成。重新发布的代码: http://weblogs.asp.net/fbouma/archive/2009/05/18/multi-value-dictionary-c-source-code-net-3-5.aspx - Frans Bouma
哎呀,ILookup<> 存在问题:它还实现了一个 Enumerator,这与 Dictionary 的 Enumerator 是不同的。当在 multivaluedictionary 上使用 Linq 操作符时,它无法选择要使用的 Enumerator,因为它还可以使用 IEnumerable<TKey, IGrouping<TKey, TValue>>,即使已经显式实现了 ILookup... 这使得使用该字典更加困难,需要进行明确的类型说明。 - Frans Bouma
MultiDictionary也存在于PowerCollections中:www.wintellect.com/powercollections.aspx - Dmitri Nesteruk

10

想要添加不同之处:

在字典中尝试访问不存在的键会导致运行时错误,但在哈希表中没有问题,因为它返回的是null而不是错误。

例如:

       //No strict type declaration
        Hashtable hash = new Hashtable();
        hash.Add(1, "One");
        hash.Add(2, "Two");
        hash.Add(3, "Three");
        hash.Add(4, "Four");
        hash.Add(5, "Five"); 
        hash.Add(6, "Six");
        hash.Add(7, "Seven");
        hash.Add(8, "Eight");
        hash.Add(9, "Nine");
        hash.Add("Ten", 10);// No error as no strict type

        for(int i=0;i<=hash.Count;i++)//=>No error for index 0
        {
            //Can be accessed through indexers
            Console.WriteLine(hash[i]);
        }
        Console.WriteLine(hash["Ten"]);//=> No error in Has Table

这里的key 0没有错误,而且key "ten"(注意:t是小写)也没有错误。

//Strict type declaration
        Dictionary<int,string> dictionary= new Dictionary<int, string>();
        dictionary.Add(1, "One");
        dictionary.Add(2, "Two");
        dictionary.Add(3, "Three");
        dictionary.Add(4, "Four");
        dictionary.Add(5, "Five");
        dictionary.Add(6, "Six");
        dictionary.Add(7, "Seven");
        dictionary.Add(8, "Eight");
        dictionary.Add(9, "Nine");
        //dictionary.Add("Ten", 10);// error as only key, value pair of type int, string can be added

        //for i=0, key doesn't  exist error
        for (int i = 1; i <= dictionary.Count; i++)
        {
            //Can be accessed through indexers
            Console.WriteLine(dictionary[i]);
        }
        //Error : The given key was not present in the dictionary.
        //Console.WriteLine(dictionary[10]);

在访问时出现错误,因为键0和键10在字典中都不存在,导致运行时错误。


如果你想在foreach和for之外的集合中操作,我建议你使用LINQ来查找FirstOrDefault项。 - Piotr Kula

6
Hashtable类是一种特定类型的字典类,使用整数值(称为哈希)来辅助存储其键。Hashtable类使用哈希来加速在集合中查找特定键。.NET中的每个对象都派生自Object类。该类支持GetHash方法,返回一个唯一标识对象的整数。总体而言,Hashtable类是一种非常高效的集合。Hashtable类唯一的问题在于它需要一些开销,对于小型集合(少于十个元素),开销可能会影响性能。

两者之间存在一些特别的差异需要考虑:

HashTable:是非泛型集合,这个集合最大的开销是它会自动对你的值进行装箱,为了获得原始值,你需要执行拆箱操作,这些会作为惩罚降低你的应用程序性能。

Dictionary:这是一种泛型集合类型,在这里没有隐式装箱,因此不需要拆箱,你将始终获得你存储的原始值,这将提高你的应用程序性能。

第二个需要考虑的区别是:

如果您尝试根据不存在的键从哈希表中访问值,则会返回null。但在字典的情况下,它会给出KeyNotFoundException。


我的评论来自MCTS 2.0书籍,但它却被负面评价了...多么讽刺啊!!!:D - Rashmi Pandit
2
这并不是非常讽刺的......Hashtable和Dictionary<,>都基于此方法,因此这并不以任何方式回答了在它们之间进行选择的问题。 - Marc Gravell
4
真正的讽刺是,我正在阅读同一本MCTS书,对应该选择哪一本感到非常困惑,然后在这里发布了帖子,结果得到的回答却是我刚刚阅读过的那段文字的形式!:) 无论如何,感谢您的回复。 - blitzkriegz

3

ILookup接口在.net 3.5中与linq一起使用。

HashTable是弱类型的基类;DictionaryBase抽象类是强类型的并在内部使用HashTable。

我发现关于Dictionary有一个奇怪的事情,当我们在Dictionary中添加多个条目时,条目添加的顺序被保留。因此,如果我对Dictionary应用foreach,我将按照插入它们的相同顺序获取记录。

然而,这在普通的HashTable中不是真的,因为当我在Hashtable中添加相同的记录时,顺序不会保持不变。据我所知,Dictionary是基于Hashtable的,如果这是真的,为什么我的Dictionary保持顺序而HashTable却没有呢?

至于它们为什么行为不同,是因为泛型字典实现了哈希表,但不是基于System.Collections.Hashtable。泛型字典实现是从列表中分配键值对。然后,通过哈希表桶进行随机访问索引。但是当它返回枚举器时,它只是按顺序遍历列表,这将是插入顺序,只要条目不被重新使用。

shiv govind Birlasoft. :)


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接