为什么在 C# 中 Dictionary 优于 Hashtable?

Why is Dictionary preferred over Hashtable in C#?

提问人:Nakul Chaudhary 提问时间:11/19/2008 最后编辑:kristianpNakul Chaudhary 更新时间:10/25/2021 访问量:600229

问:

在大多数编程语言中,字典比哈希表更受欢迎。 这背后的原因是什么?

C# .NET vb.net 数据结构

评论

32赞 Promit 4/17/2009
> 这不一定是真的。哈希表是字典的实现。一个典型的,它可能是 .NET 中的默认一个,但根据定义,它不是唯一的一个。我不确定这是否是 ECMA 标准所要求的,但 MSDN 文档非常清楚地指出它是作为哈希表实现的。它们甚至在替代方案更合理时提供 SortedList 类。
24赞 arkon 3/21/2015
@Promit我一直认为这是 .DictionaryHashtable
2赞 Radinator 8/4/2016
我认为原因是,在字典中,您可以定义键的类型和自拍的值。Hashtable 只能接受对象,并根据哈希值保存对(from object.GetHashCode() )。
0赞 kristianp 3/6/2019
该问题的原始标题是特定于 c# 的。我已将“in c#”恢复到标题中。
2赞 jrh 3/28/2019
不要与 HashSet<T>混淆,后者与 不同,是通用的。HashTable

答:

195赞 gius 11/19/2008 #1

因为是一个泛型类 ( ),因此访问其内容是类型安全的(即您不需要像使用 )。DictionaryDictionary<TKey, TValue>ObjectHashtable

比较

var customers = new Dictionary<string, Customer>();
...
Customer customer = customers["Ali G"];

var customers = new Hashtable();
...
Customer customer = customers["Ali G"] as Customer;

但是,在内部实现为哈希表,因此从技术上讲,它的工作方式相同。Dictionary

72赞 Marc Gravell 11/19/2008 #2

在 .NET 中,和之间的区别主要在于前者是泛型类型,因此在静态类型检查方面可以获得泛型的所有好处(并减少了装箱,但这并不像人们在性能方面倾向于认为的那么大 - 不过,装箱有一定的内存成本)。Dictionary<,>HashTable

18赞 flesh 11/19/2008 #3

是松散类型的数据结构,因此您可以向 .该类是类型安全的实现,键和值是强类型的。创建实例时,必须同时指定键和值的数据类型。HashtableHashtableDictionaryHashtableDictionary

1712赞 Michael Madsen 11/19/2008 #4

就其价值而言,字典(从概念上讲)是一个哈希表。

如果你的意思是“为什么我们使用类而不是类?”,那么这是一个简单的答案:是泛型类型,不是。这意味着你可以用 获得类型安全,因为你不能在其中插入任何随机对象,也不必强制转换你取出的值。Dictionary<TKey, TValue>HashtableDictionary<TKey, TValue>HashtableDictionary<TKey, TValue>

有趣的是,.NET Framework 中的实现基于 ,从源代码中的注释中可以看出:Dictionary<TKey, TValue>Hashtable

通用字典是从 Hashtable 的源代码复制而来的

评论

442赞 Chris S 1/26/2009
而且通用集合的速度也快得多,因为没有装箱/拆箱
7赞 Chris S 1/26/2009
不确定带有上述语句的 Hashtable,但对于 ArrayList 与 List<t>这是真的
41赞 Guvante 4/17/2009
Hashtable 使用 Object 在内部保存东西(只有非通用的方式),所以它也必须装箱/拆箱。
22赞 Michael Madsen 6/21/2013
@BrianJ:“哈希表”(两个词)是这种结构的计算机科学术语;字典是一种特定的实现。HashTable 大致对应于 Dictionary<object,object>(尽管接口略有不同),但两者都是哈希表概念的实现。当然,为了进一步混淆问题,一些语言称它们的哈希表为“字典”(例如 Python)——但正确的 CS 术语仍然是哈希表。
40赞 Michael Madsen 6/21/2013
@BrianJ:(类)和(类)都是哈希表(概念),但 a 不是 ,也不是 。它们的使用方式非常相似,并且可以以与 a 相同的非类型化方式运行,但它们不直接共享任何代码(尽管部分可能以非常相似的方式实现)。HashTableDictionaryHashTableDictionaryDictionaryHashTableDictionary<Object,Object>HashTable
97赞 user38902 11/19/2008 #5

仅供参考:在 .NET 中,线程安全供多个读取器线程和一个写入线程使用,而在公共中,静态成员是线程安全的,但不能保证任何实例成员都是线程安全的。HashtableDictionary

因此,我们不得不将所有词典改回原来的字典。Hashtable

评论

13赞 Triynko 5/15/2010
乐趣。Dictionary<T> 源代码看起来更简洁、更快。最好使用 Dictionary 并实现自己的同步。如果字典的读取绝对需要是最新的,那么你只需要同步对字典的读/写方法的访问。这将是很多锁定,但这是正确的。
11赞 Triynko 5/15/2010
或者,如果你的读数不必是绝对最新的,你可以将字典视为不可变的。然后,您可以获取对字典的引用,并通过完全不同步读取来获得性能(因为它是不可变的,并且本质上是线程安全的)。若要更新它,请在后台构造字典的完整更新副本,然后只需将引用与 Interlocked.CompareExchange 交换(假设只有一个写入线程;多个写入线程需要同步更新)。
47赞 Dan Is Fiddling By Firelight 1/28/2012
.Net 4.0 添加了一个类,该类将所有公共/受保护的方法实现为线程安全。如果您不需要支持旧平台,这将允许您替换多线程代码: msdn.microsoft.com/en-us/library/dd287191.aspxConcurrentDictionaryHashtable
6赞 supercat 11/14/2013
我记得读到过 HashTable 仅在从不从表中删除信息的情况下是读写器线程安全的。如果读者在删除其他项目时请求表格中的项目,并且读者会在多个位置查找该项目,则在读者搜索时,作者可能会将该项目从尚未检查的位置移动到已检查的位置, 从而导致该项目不存在的虚假报告。
35赞 rix0rrr 11/19/2008 #6

人们说字典和哈希表是一样的。

这不一定是真的。哈希表是实现字典的一种方法。一个典型的,它可能是类中 .NET 中的默认一个,但根据定义,它不是唯一的一个。Dictionary

你同样可以使用链表或搜索树来实现字典,只是效率不高(对于某些效率指标)。

评论

4赞 snemarch 9/23/2010
MS 文档说:“使用其键检索值非常快,接近 O(1),因为 Dictionary <(Of <(TKey, TValue >)>) 类是作为哈希表实现的。 可以是任何东西,尽管:)Dictionary<K,V>IDictionary<K,V>
15赞 Joseph Hamilton 8/10/2011
@rix0rrr - 我认为你已经倒过来了,字典使用哈希表,而不是哈希表使用字典。
8赞 Robert Hensing 3/8/2013
@JosephHamilton - rix0rrr 说得对:“哈希表字典的实现。他指的是“字典”的概念,而不是类(注意小写)。从概念上讲,哈希表实现字典接口。在 .NET 中,Dictionary 使用哈希表来实现 IDictionary。这是凌乱的;)
0赞 Joseph Hamilton 10/15/2014
我说的是 .NET,因为这是他在回复中提到的。
2赞 ToolmakerSteve 3/23/2015
@JosephHamilton:实现(或实现)甚至与使用并不意味着相同的东西。恰恰相反。如果他稍微不同(但意思相同)的话,也许会更清楚:“哈希表是实现字典的一种方式”。也就是说,如果你想要字典的功能,一种方法(实现字典)是使用哈希表。
5赞 prashant 4/17/2009 #7

我能弄清楚的另一个区别是:

我们不能将 Dictionary<KT,VT>(泛型)用于 Web 服务。原因是没有 Web 服务标准支持泛型标准。

评论

0赞 Siddharth 11/30/2015
我们可以在基于 soap 的 Web 服务中使用通用列表 (List<string>)。但是,我们不能在 Web 服务中使用字典(或哈希表)。我认为这样做的原因是.net xmlserializer无法处理字典对象。
18赞 Brant 1/25/2010 #8

请注意,文档说:“Dictionary<(Of <(TKey, TValue>)>) 类是作为哈希表实现的”,而不是“Dictionary<(Of <(TKey, TValue>)>) 类是作为 HashTable 实现的"

Dictionary 不是作为 HashTable 实现的,而是按照哈希表的概念实现的。由于使用了泛型,该实现与 HashTable 类无关,尽管 Microsoft 内部可以使用相同的代码并将 Object 类型的符号替换为 TKey 和 TValue。

在 .NET 1.0 中,泛型不存在;这是 HashTable 和 ArrayList 最初开始的地方。

-3赞 Yuriy Zaletskyy 3/9/2010 #9

根据我使用 .NET Reflector 所看到的:

[Serializable, ComVisible(true)]
public abstract class DictionaryBase : IDictionary, ICollection, IEnumerable
{
    // Fields
    private Hashtable hashtable;

    // Methods
    protected DictionaryBase();
    public void Clear();
.
.
.
}
Take note of these lines
// Fields
private Hashtable hashtable;

因此,我们可以确定 DictionaryBase 在内部使用了 HashTable。

评论

18赞 snemarch 9/23/2010
System.Collections.Generic.Dictionary<TKey,TValue> 不是从 DictionaryBase 派生的。
0赞 Jim Balter 11/4/2018
“因此,我们可以确定 DictionaryBase 在内部使用了 HashTable”——这很好,但这与问题无关。
700赞 Marcel Toth 4/21/2011 #10

差异

Dictionary Hashtable
通用 非通用
需要自己的线程同步 通过 Synchronized() 方法提供线程安全版本
枚举项:KeyValuePair 枚举项:DictionaryEntry
较新的 (> .NET 2.0) 较旧(自 .NET 1.0 起))
位于 System.Collections.Generic 位于 System.Collections
对不存在的密钥的请求引发异常 对不存在的键的请求返回 null
对于值类型,可能会更快一些 值类型稍(需要装箱/取消装箱)

相似 之 处:

  • 两者都是内部哈希表 == 根据键快速访问多项目数据
  • 两者都需要不可变且唯一的密钥
  • 两者的键都需要自己的 GetHashCode() 方法

备用 .NET 集合:

(用于代替 Dictionary 和 Hashtable 的候选者)

  • ConcurrentDictionary - 线程安全(可以同时从多个线程安全地访问)
  • HybridDictionary - 优化性能(针对少数项目和许多项目)
  • OrderedDictionary- 可以通过 int 索引访问值(按添加项的顺序)
  • SortedDictionary- 项目自动排序
  • StringDictionary- 强类型并针对字符串进行了优化(现已弃用,取而代之的是 Dictionary<string,string>)

评论

13赞 Trident D'Gao 6/30/2013
@Guillaume86,这就是使用 TryGetValue 而不是 msdn.microsoft.com/en-us/library/bb347013.aspx
3赞 Cheng Chen 6/25/2014
+1 表示 ...顺便说一句,与使用默认构造函数时不同。StringDictionaryStringDictionaryDictionary<string, string>
0赞 VoteCoffee 10/31/2014
ParallelExtensionsExtras @code.msdn.microsoft.com/windowsdesktop/... 包含一个 ObservableConcurrentDictionary,它既是很好的 fir 绑定,也是并发的。
0赞 Steve Dunn 3/17/2021
StringDictionary现在被认为已过时,取而代之的是具有合适实例的 *Dictionary<string,string>StringComparer
5赞 Kishore Kumar 5/22/2012 #11

Dictionary<>是泛型类型,因此它是类型安全的。

您可以在 HashTable 中插入任何值类型,这有时可能会引发异常。但只接受整数值,同样也只接受字符串。Dictionary<int>Dictionary<string>

因此,最好使用而不是 .Dictionary<>HashTable

24赞 Sujit 9/17/2012 #12

Collections & Generics对于处理对象组很有用。在 .NET 中,所有集合对象都位于接口下,而接口又具有 & 。在 .NET Framework 2.0 之后,& 被替换为 &。现在,&在当今的项目中不再使用。IEnumerableArrayList(Index-Value))HashTable(Key-Value)ArrayListHashTableListDictionaryArraylistHashTable

谈到 & 之间的区别,是通用的,而 as 不是通用的。我们可以向 添加任何类型的对象,但在检索时,我们需要将其转换为所需的类型。因此,它不是类型安全的。但是要 ,在声明自身的同时,我们可以指定键和值的类型,因此在检索时无需强制转换。HashTableDictionaryDictionaryHastableHashTabledictionary

让我们看一个例子:

哈希表

class HashTableProgram
{
    static void Main(string[] args)
    {
        Hashtable ht = new Hashtable();
        ht.Add(1, "One");
        ht.Add(2, "Two");
        ht.Add(3, "Three");
        foreach (DictionaryEntry de in ht)
        {
            int Key = (int)de.Key; //Casting
            string value = de.Value.ToString(); //Casting
            Console.WriteLine(Key + " " + value);
        }

    }
}

字典

class DictionaryProgram
{
    static void Main(string[] args)
    {
        Dictionary<int, string> dt = new Dictionary<int, string>();
        dt.Add(1, "One");
        dt.Add(2, "Two");
        dt.Add(3, "Three");
        foreach (KeyValuePair<int, String> kv in dt)
        {
            Console.WriteLine(kv.Key + " " + kv.Value);
        }
    }
}

评论

2赞 Ron 7/13/2016
我们可以使用 var,而不是显式分配 KeyValuePair 的数据类型。因此,这将减少键入 - foreach (var kv in dt)...只是一个建议。
19赞 Oliver 1/15/2013 #13

从 .NET Framework 3.5 开始,还有一个 HashSet<T>它提供了 Dictionary<TKey、TValue 的所有优点>如果您只需要键而不需要值。

因此,如果您使用 并且始终将值设置为 来模拟类型安全哈希表,您可能应该考虑切换到 HashSet<T>Dictionary<MyType, object>null

9赞 mparkuk 5/1/2014 #14

Hashtable 对象由包含集合元素的存储桶组成。存储桶是 Hashtable 中元素的虚拟子组,与大多数集合相比,它使搜索和检索更容易、更快捷

Dictionary 类具有与 Hashtable 类相同的功能。对于值类型,特定类型(Object 除外)的 Dictionary 比 Hashtable 具有更好的性能,因为 Hashtable 的元素属于 Object 类型,因此,在存储或检索值类型时,通常会发生装箱和取消装箱。

进一步阅读:哈希表和字典集合类型

18赞 Altaf Patel 5/28/2014 #15

字典:

  • 如果我们尝试查找不存在的键,它会返回/抛出异常。

  • 它比 Hashtable 更快,因为没有装箱和拆箱。

  • 只有公共静态成员是线程安全的。

  • Dictionary 是一种泛型类型,这意味着我们可以将它与任何数据类型一起使用(创建时,必须指定键和值的数据类型)。

    例:Dictionary<string, string> <NameOfDictionaryVar> = new Dictionary<string, string>();

  • Dictionay 是 Hashtable 的类型安全实现,并且是强类型的。KeysValues

哈希表:

  • 如果我们尝试查找不存在的键,它将返回 null。

  • 它比字典慢,因为它需要装箱和解箱。

  • Hashtable 中的所有成员都是线程安全的,

  • Hashtable 不是泛型类型,

  • Hashtable 是松散类型的数据结构,我们可以添加任何类型的键和值。

评论

0赞 Jim Balter 11/4/2018
“如果我们尝试查找不存在的键,它会返回/抛出异常。”如果您使用Dictionary.TryGetValue
21赞 alexandrekow 10/29/2014 #16

MSDN 上的 Extensive Examination of Data Structures Using C# 一文指出,冲突解决策略也存在差异:

Hashtable 类使用一种称为重新散列的技术。

重新散列的工作原理如下:有一组散列不同的函数, H1 ...Hn,以及从哈希中插入或检索项目时 表,最初使用 H1 哈希函数。如果这会导致 碰撞,则尝试 H2,如果需要,可以继续到 Hn

字典使用一种称为链接的技术。

通过重新散列,在发生冲突时,将重新计算哈希值,并尝试与哈希对应的新槽。然而,通过链接,使用辅助数据结构来保存 任何碰撞。具体来说,字典中的每个插槽都有一个数组 映射到该存储桶的元素。如果发生碰撞, 碰撞元素将追加到存储桶列表的前面。

8赞 NullReference 11/12/2015 #17

另一个重要的区别是 Hashtable 是线程安全的。Hashtable 具有内置的多读取器/单写入器 (MR/SW) 线程安全,这意味着 Hashtable 允许一个写入器与多个读取器一起使用而不会锁定。

在 Dictionary 的情况下,没有线程安全;如果需要线程安全,则必须实现自己的同步。

进一步阐述:

Hashtable 通过属性提供一些线程安全,该属性在集合周围返回线程安全包装器。包装器的工作原理是在每次添加或删除操作时锁定整个集合。因此,尝试访问集合的每个线程都必须等待轮到它来获取一个锁。这是不可缩放的,并且可能会导致大型集合的性能显著下降。此外,该设计并未完全受到竞争条件的影响。Synchronized

.NET Framework 2.0 集合类(如 等)不提供任何线程同步;在多个线程上同时添加或删除项时,用户代码必须提供所有同步List<T>, Dictionary<TKey, TValue>

如果需要类型安全和线程安全,请在 .NET Framework 中使用并发集合类。进一步阅读 这里.

另一个区别是,当我们在 Dictionary 中添加多个条目时,条目的添加顺序保持不变。当我们从 Dictionary 中检索项目时,我们将按照插入记录的相同顺序获取记录。而 Hashtable 不保留广告订单。

评论

0赞 supercat 2/15/2020
据我了解,在不涉及删除的使用场景中,保证了 MR/SW 线程的安全性。我认为它可能旨在完全实现 MR/SW 安全,但安全处理删除会大大增加 MR/SW 安全的费用。虽然设计可以在无删除场景中以最低的成本提供 MR/SW 安全性,但我认为 MS 希望避免将无删除场景视为“特殊”。HashsetDictionary
10赞 Siva Sankar Gorantla 7/13/2016 #18

哈希表:

键/值在存储到堆中时将转换为对象(装箱)类型。

从堆中读取时,需要将键/值转换为所需的类型。

这些操作的成本非常高。我们需要尽可能避免装箱/拆箱。

字典:HashTable 的泛型变体。

没有装箱/拆箱。无需转换。

0赞 kristianp 3/6/2019 #19

在大多数编程语言中,字典比哈希表更受欢迎

我不认为这一定是真的,大多数语言都有这样或那样,这取决于他们喜欢的术语

然而,在 C# 中,明确的原因(对我来说)是 C# HashTables 和 System.Collections 命名空间的其他成员在很大程度上已经过时了。它们存在于 c# V1.1 中。它们已从 C# 2.0 替换为 System.Collections.Generic 命名空间中的 Generic 类。

评论

0赞 Bill Norman 3/21/2019
与字典相比,哈希表的优点之一是,如果字典中不存在键,它将抛出错误。如果哈希表中不存在键,则它只返回 null。
0赞 kristianp 3/21/2019
在 C# 中,我仍然会避免使用 System.Collections.Hashtable,因为它们没有泛型的优势。如果您不知道密钥是否存在,可以使用 Dictionary 的 TryGetValue 或 HasKey。
0赞 kristianp 3/21/2019
哎呀,不是 HasKey,它应该是 ContainsKey。