ChatGPT解决这个技术问题 Extra ChatGPT

为什么字典在 C# 中优于 Hashtable?

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

>这不一定是真的。哈希表是字典的实现。一个典型的,它可能是 .NET 中的默认值,但根据定义它不是唯一的。我不确定这是否是 ECMA 标准所要求的,但 MSDN documentation 非常清楚地将其称为作为哈希表实现。当替代方案更合理时,它们甚至提供 SortedList 类。
@Promit 我一直认为 DictionaryHashtable 的实现。
我认为原因是,您可以在字典中定义键的类型和 selfe 的值。 Hashtable 只能获取对象并根据哈希值保存对(来自 object.GetHashCode() )。
问题的原始标题是特定于 c# 的。我已将“in c#”恢复为标题。
不要与 HashSet<T> 混淆,它与 HashTable 不同,是通用的。

a
adjan

对于它的价值,字典是(概念上)一个哈希表。

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

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

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

Source


由于没有装箱/拆箱,通用集合也快得多
不确定带有上述语句的 Hashtable,但对于 ArrayList vs List 这是真的
Hashtable 使用 Object 在内部保存东西(只有非通用的方法)所以它也必须装箱/拆箱。
@BrianJ:“哈希表”(两个词)是这种结构的计算机科学术语;字典是一个具体的实现。 HashTable 大致对应于 Dictionary (尽管接口略有不同),但两者都是哈希表概念的实现。当然,为了进一步混淆问题,一些语言称他们的哈希表为“字典”(例如 Python)——但正确的 CS 术语仍然是哈希表。
@BrianJ:HashTable(类)和 Dictionary(类)都是哈希表(概念),但 HashTable 不是 DictionaryDictionary 也不是 HashTable。它们以非常相似的方式使用,并且 Dictionary<Object,Object> 可以以与 HashTable 相同的无类型方式运行,但它们不直接共享任何代码(尽管部分可能以非常相似的方式实现)。
K
KyleMit

差异

Dictionary Hashtable Generic Non-Generic 需要自己的线程同步 通过 Synchronized() 方法提供线程安全版本 Enumerated item: KeyValuePair Enumerated item: DictionaryEntry Newer (> .NET 2.0) Older (since .NET 1.0) is in System.Collections.Generic is in System.Collections 对不存在的键的请求引发异常 对不存在的键的请求返回 null 对于值类型可能快一点 对于值类型慢一点(需要装箱/拆箱)

相似之处:

两者都是内部哈希表 == 根据键快速访问多项数据

两者都需要不可变和唯一的键

两者的键都需要自己的 GetHashCode() 方法

替代 .NET 集合:

(可以代替 Dictionary 和 Hashtable 使用的候选人)

ConcurrentDictionary - 线程安全(可以同时从多个线程安全访问)

HybridDictionary - 优化的性能(对于少数项目和许多项目)

OrderedDictionary - 可以通过 int index 访问值(按添加项目的顺序)

SortedDictionary - 自动排序的项目

StringDictionary - 为字符串强类型化和优化(现在不推荐使用 Dictionary


@Guillaume86,这就是您使用 TryGetValue 的原因msdn.microsoft.com/en-us/library/bb347013.aspx
当您使用默认构造函数时,为 StringDictionary...btw StringDictionary +1 与 Dictionary<string, string> 不同。
ParallelExtensionsExtras @code.msdn.microsoft.com/windowsdesktop/… 包含一个 ObservableConcurrentDictionary,它是很好的绑定和并发性。
很棒的解释,很高兴您还列出了相似之处以减少可能出现的问题
S
StayOnTarget

因为 Dictionary 是一个通用类 ( Dictionary<TKey, TValue> ),所以访问它的内容是类型安全的(即您不需要像 Hashtable 那样从 Object 进行转换)。

相比

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

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

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


P
Peter Mortensen

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

因此,我们不得不将所有字典改回 Hashtable


乐趣。 Dictionary 源代码看起来更加简洁和快速。使用 Dictionary 并实现自己的同步可能会更好。如果字典读取绝对需要是最新的,那么您只需同步对字典的读/写方法的访问。这将是很多锁定,但它会是正确的。
或者,如果您的阅读不必绝对是最新的,您可以将字典视为不可变的。然后,您可以通过根本不同步读取来获取对 Dictionary 的引用并获得性能(因为它是不可变的并且本质上是线程安全的)。要更新它,您在后台构建字典的完整更新副本,然后只需将引用与 Interlocked.CompareExchange 交换(假设单个写入线程;多个写入线程将需要同步更新)。
.Net 4.0 添加了 ConcurrentDictionary 类,该类将所有公共/受保护的方法实现为线程安全的。如果您不需要支持旧平台,这可以让您替换多线程代码中的 Hashtablemsdn.microsoft.com/en-us/library/dd287191.aspx
我记得在从未从表中删除信息的情况下,HashTable 只是读写器线程安全的。如果读者在删除另一个项目时询问表中的项目,并且读者会在多个位置查找该项目,则可能在阅读器搜索时,作者可能会移动该项目从一个没有被检查的地方到一个已经检查过的地方,从而导致该项目不存在的虚假报告。
M
Marc Gravell

在 .NET 中,Dictionary<,>HashTable 之间的区别主要在于前者是泛型类型,因此您可以在静态类型检查方面获得泛型的所有好处(并减少装箱,但这并没有那么大正如人们倾向于从性能方面考虑的那样——尽管拳击有一定的内存成本)。


S
StayOnTarget

人们说字典与哈希表相同。

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

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


MS 文档说:“使用它的键检索一个值非常快,接近 O(1),因为 Dictionary <(Of <(TKey, TValue >)>) 类被实现为哈希表。” - 因此在处理 Dictionary<K,V> 时应该保证您是哈希表。 IDictionary<K,V> 可以是任何东西,但 :)
@rix0rrr - 我认为你已经倒退了,字典使用哈希表而不是哈希表使用字典。
@JosephHamilton - rix0rrr 做对了:“哈希表是字典的实现。”他的意思是“字典”的概念,而不是类(注意小写)。从概念上讲,哈希表实现了字典接口。在 .NET 中,Dictionary 使用哈希表来实现 IDictionary。很乱;)
我在 .NET 中谈论,因为这是他在回复中引用的内容。
@JosephHamilton:实现(或实现)与使用的意思完全不同。恰恰相反。如果他说的稍有不同(但含义相同),也许会更清楚:“哈希表是实现字典的一种方式”。也就是说,如果您想要字典的功能,一种方法(实现字典)是使用哈希表。
P
Peter Mortensen

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

HashTable & 之间的区别DictionaryDictionary 是通用的,而 Hastable 不是通用的。我们可以将任何类型的对象添加到 HashTable,但在检索时我们需要将其转换为所需的类型。所以,它不是类型安全的。但是对于dictionary,我们可以在声明自己的同时指定key和value的类型,所以不需要在检索的时候进行强制转换。

让我们看一个例子:

哈希表

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);
        }
    }
}

我们可以使用 var,而不是显式地为 KeyValuePair 分配数据类型。所以,这会减少打字 - foreach (var kv in dt)...只是一个建议。
M
MarmiK

字典:

如果我们试图找到一个不存在的键,它会返回/抛出异常。

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

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

Dictionary 是一种通用类型,这意味着我们可以将它与任何数据类型一起使用(创建时,必须为键和值指定数据类型)。示例: Dictionary = new Dictionary();

Dictionay 是 Hashtable 的类型安全实现,键和值是强类型的。

哈希表:

如果我们试图找到一个不存在的键,它会返回 null。

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

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

Hashtable 不是泛型类型,

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


“如果我们试图找到一个不存在的键,它会返回/抛出异常。”如果您使用 Dictionary.TryGetValue,则不会
g
g t

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

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

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

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

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


P
Peter Mortensen

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

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


N
Nate Barbettini

Hashtable 是一种松散类型的数据结构,因此您可以将任何类型的键和值添加到 HashtableDictionary 类是类型安全的 Hashtable 实现,键和值是强类型的。创建 Dictionary 实例时,您必须指定键和值的数据类型。


A
Andrew Morton

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

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

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


P
Peter Mortensen

哈希表:

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

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

这些操作非常昂贵。我们需要尽可能避免装箱/拆箱。

Dictionary : HashTable 的通用变体。

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


N
NullReference

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

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

进一步阅读:Hashtable and Dictionary Collection Types


P
Peter Mortensen

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

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

进一步阐述:

Hashtable 通过 Synchronized 属性提供一些线程安全性,该属性返回集合周围的线程安全包装器。包装器通过在每次添加或删除操作时锁定整个集合来工作。因此,每个试图访问集合的线程都必须等待轮到它来获取一个锁。这是不可扩展的,并且可能导致大型集合的性能显着下降。此外,该设计并未完全免受竞争条件的影响。 List、Dictionary 等 .NET Framework 2.0 集合类不提供任何线程同步;当在多个线程上同时添加或删除项目时,用户代码必须提供所有同步

如果您需要类型安全以及线程安全,请使用 .NET Framework 中的并发集合类。进一步阅读here

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


据我了解,Hashset不涉及删除的使用场景中保证 MR/SW 线程安全。我认为这可能是为了完全保证 MR/SW 安全,但是安全地处理删除大大增加了 MR/SW 安全的费用。虽然 Dictionary 的设计可以在不可删除场景中以最低成本提供 MR/SW 安全性,但我认为 MS 希望避免将不可删除场景视为“特殊”。
P
Peter Mortensen

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

我们不能在 Web 服务中使用 Dictionary(泛型)。原因是没有 Web 服务标准支持泛型标准。


我们可以在基于SOAP 的Web 服务中使用通用列表(List)。但是,我们不能在 Web 服务中使用字典(或哈希表)。我认为原因是.net xmlserializer 无法处理字典对象。
P
Peter Mortensen

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

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

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


k
kristianp

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

我认为这不一定是正确的,大多数语言都有其中一种,具体取决于 terminology they prefer

然而,在 C# 中,(对我而言)明显的原因是 C# HashTables 和 System.Collections 命名空间的其他成员在很大程度上已经过时。它们出现在 c# V1.1 中。从 C# 2.0 开始,它们已被 System.Collections.Generic 命名空间中的通用类取代。


哈希表相对于字典的优点之一是,如果字典中不存在某个键,则会引发错误。如果哈希表中不存在某个键,则它只返回 null。
在 C# 中,我仍然会避免使用 System.Collections.Hashtable,因为它们没有泛型的优势。如果您不知道密钥是否存在,可以使用 Dictionary 的 TryGetValue 或 HasKey。
哎呀,不是 HasKey,应该是 ContainsKey。
P
Peter Mortensen

根据我使用 .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。


System.Collections.Generic.Dictionary 不是从 DictionaryBase 派生的。
“所以我们可以确定 DictionaryBase 在内部使用 HashTable。” -- 这很好,但它与问题无关。