为什么字典偏好Hashtable?
在大多数编程语言中,字典比hashtables更受欢迎。 这背后的原因是什么?
值得一提的是,Dictionary 是一个哈希表。
如果你的意思是“为什么我们使用Dictionary类而不是Hashtable类?”,那么这是一个简单的答案:Dictionary是一个genericstypes,而Hashtable则不是。 这意味着你可以通过Dictionary获得types安全性,因为你不能插入任何随机的对象,而且你不需要转换你所取的值。
Dictionary
<<< >>> Hashtable
差异:
- generics <<< >>> 非generics
- 需要自己的线程同步 <<< >>>通过
Synchronized()
方法提供线程安全版本 - 枚举项:
KeyValuePair
<<< >>>枚举项:DictionaryEntry
- 较新(> .NET 2.0 )<<< >>>较老(从.NET 1.0开始 )
- 在System.Collections.Generic <<< >>>在System.Collections中
- 请求到不存在的键抛出exception <<< >>>请求到不存在的键返回null
- 对于值types来说可能会稍微快 一点 (对于值types来说,需要装箱/取消装箱)
Dictionary
/ Hashtable
相似之处:
- 两者都是内部哈希表 ==根据关键字快速访问多项目数据
- 两者都需要不可变的唯一键
- 两者的键都需要自己的
GetHashCode()
方法
类似的 .NET集合(用来代替Dictionary和Hashtable的候选):
-
ConcurrentDictionary
– 线程安全 (可以安全地从多个线程同时访问) -
HybridDictionary
– 优化的性能 (对于less数项目和许多项目) -
OrderedDictionary
– 可以通过int索引访问值(按照添加项目的顺序) -
SortedDictionary
– 自动sorting的项目 -
StringDictionary
– 为string强types化和优化
因为Dictionary
是一个generics类( Dictionary<TKey, TValue>
),所以访问它的内容是types安全的(即,你不需要从Object
,就像使用Hashtable
)。
比较
var customers = new Dictionary<string, Customer>(); ... Customer customer = customers["Ali G"];
至
var customers = new Hashtable(); ... Customer customer = customers["Ali G"] as Customer;
然而, Dictionary
是作为Hashtable
内部实现的,所以在技术上它的工作方式是一样的。
FYI:在.NET中, Hashtable
是线程安全的,可供多个阅读器线程和单个写入线程使用,而在Dictionary
public static members中是线程安全的,但不保证所有实例成员都是线程安全的。
因此,我们必须把所有的字典都改回Hashtable
。
在.NET中, Dictionary<,>
和HashTable
之间的区别主要在于前者是一个genericstypes,所以在静态types检查(和减less的装箱方面)方面你可以获得generics的所有好处,但是这并不像人们往往在performance方面考虑 – 虽然拳击有一定的记忆成本)。
人们在说一个字典和一个哈希表是一样的。
这不一定是真的。 哈希表是字典的一个实现 。 这是一个典型的例子,它可能是.NET中默认的一个,但它不是唯一的定义。
你也可以很好地实现一个链接列表或search树的字典,它只是不会有效率(一些有效的度量)。
Collections
& Generics
对于处理一组对象很有用。 在.NET中,所有集合对象都在IEnumerable
接口下,而IEnumerable
接口又有ArrayList(Index-Value))
& HashTable(Key-Value)
。 在.NET框架2.0之后, ArrayList
& HashTable
被replace为List
& Dictionary
。 现在, Arraylist
& HashTable
在现在的项目中已经不再使用了。
来到HashTable
& Dictionary
之间的区别, Dictionary
是通用的,因为Hastable
不是通用的。 我们可以添加任何types的对象到HashTable
,但是在检索时,我们需要将它转换为所需的types。 所以,这不是types安全的。 但是对于dictionary
,在声明自己的同时我们可以指定键和值的types,所以在检索时不需要进行强制转换。
我们来看一个例子:
哈希表
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); } } }
字典:
-
它返回/抛出exception,如果我们试图find一个不存在的关键。
-
它比哈希表快,因为没有装箱和拆箱。
-
只有公共静态成员是线程安全的。
-
字典是一个genericstypes,这意味着我们可以将它用于任何数据types(创build时,必须为键和值指定数据types)。
示例:
Dictionary<string, string> <NameOfDictionaryVar> = new Dictionary<string, string>();
-
Dictionay是Hashtable的types安全实现,
Keys
和Values
是强types的。
哈希表:
-
如果我们试图find一个不存在的键,它将返回null。
-
它比字典慢,因为它需要装箱和拆箱。
-
所有Hashtable中的成员都是线程安全的,
-
哈希表不是genericstypes,
-
哈希表是松散types的数据结构,我们可以添加任何types的键和值。
数据结构的广泛检查使用 MSDN上的C#文章指出,在冲突解决策略方面也有所不同:
Hashtable类使用了一种称为重新哈希的技术。
重新哈希函数的工作原理如下:有一组哈希函数H 1 … H n ,当从哈希表中插入或检索项目时,首先使用H 1哈希函数。 如果这导致碰撞,则尝试H 2 ,如果需要则向上H n 。
词典使用了一种称为链接的技术。
在重新哈希的情况下,如果发生冲突,则重新计算哈希,并尝试对应于哈希的新时隙。 然而,链接使用二级数据结构来保存任何冲突 。 具体而言,Dictionary中的每个插槽都有一个映射到该存储桶的元素数组。 如果发生冲突,冲突的元素将被添加到存储桶列表中。
由于.NET Framework 3.5还有一个HashSet<T>
,它提供了Dictionary<TKey, TValue>
所有优点,如果只需要键和值。
所以,如果你使用Dictionary<MyType, object>
并且总是把值设置为null
来模拟types安全哈希表,那么你应该考虑切换到HashSet<T>
。
Hashtable
是一个松散types的数据结构,所以你可以将任何types的键和值添加到Hashtable
。 Dictionary
类是一个types安全的Hashtable
实现,键和值是强types的。 创buildDictionary
实例时,必须为键和值指定数据types。
请注意,MSDN说:“Dictionary <(Of <(TKey,TValue>)>)类被实现为散列表 ”,而不是“Dictionary <(Of <(TKey,TValue>)>
字典不是作为一个HashTable来实现的,但它是按照一个哈希表的概念来实现的。 由于使用generics,实现与HashTable类无关,尽pipe内部Microsoft可能使用了相同的代码,并用TKey和TValuereplace了Objecttypes的符号。
在.NET 1.0generics不存在; 这是HashTable和ArrayList最初开始的地方。
哈希表对象由包含集合元素的存储桶组成。 一个存储桶是Hashtable中的一个虚拟子组, 这使得search和检索比大多数集合更容易和更快 。
Dictionary类与Hashtable类具有相同的function。 对于值types,特定types(除Object之外)的字典具有比Hashtable更好的性能 ,因为Hashtable的元素的types为Object,因此,如果存储或检索值types,通常会发生装箱和拆箱。
进一步阅读: 散列表和字典集合types
我能弄清的另一个区别是:
我们不能在Web服务中使用Dictionary <KT,VT>(generics)。 原因是没有networking服务标准支持generics标准。
Dictionary<>
是一个genericstypes,所以它是types安全的。
您可以在HashTable中插入任何值types,有时可能会引发exception。 但是Dictionary<int>
只接受整数值,而Dictionary<string>
只接受string。
所以,最好使用Dictionary<>
而不是HashTable
。
另一个重要的区别是Hashtable是线程安全的。 Hashtable内置多个读写器(MR / SW)线程安全性,这意味着Hashtable允许一个写入器与多个读卡器一起不locking。
在字典的情况下,没有线程安全; 如果你需要线程安全,你必须实现你自己的同步。
进一步阐述:
Hashtable通过
Synchronized
属性提供了一些线程安全性,它在集合周围返回一个线程安全的包装器。 包装器通过在每个添加或删除操作上locking整个集合来工作。 因此,试图访问集合的每个线程都必须等待轮到锁。 这不可扩展,并可能导致大型集合显着的性能下降。 而且,这种devise也没有完全免受竞赛的影响。.NET Framework 2.0集合类(如
List<T>, Dictionary<TKey, TValue>
等)不提供任何线程同步; 当多个线程同时添加或删除项目时,用户代码必须提供所有同步
如果您需要types安全以及线程安全性,请使用.NET Framework中的并发集合类。 进一步阅读这里 。
另一个区别是,当我们在Dictionary中添加多个条目时,条目的添加顺序被保留。 当我们从Dictionary中检索这些项目时,我们将以与插入它们相同的顺序得到这些logging。 而Hashtable不保留插入顺序。
哈希表:
键/值将被转换为对象(装箱)types,同时存储到堆中。
键/值需要从堆中读取时转换为所需的types。
这些操作非常昂贵。 我们需要尽可能地避免装箱/拆箱。
字典: HashTable的generics变体。
没有拳击/拆箱。 无需转换
根据我所看到的使用.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。