首先解释什么是哈希表。
哈希表是类似<key,value>的一个键值对集合, key和 value都属于object类型。也就反应了它存在的一个问题:类型安全问题。
比如我可以hs.add("name","susu");同样可以添加hs.add("age",28).这样的话,在访问时就存在类型错误的问题。
而Dictionary,属于Hashtable的一种泛型实现,保证了类型安全问题。也就是如果存储的类型是值类型,那就避免了装箱操作。那
Dictionary内部是一个怎样的实现过程呢。
首先这里附上我参考的博文,我这里只说大概,原来还是看人家的。
https://www.youkuaiyun.com/gather_21/MtTagg4sMDM4LWJsb2cO0O0O.html
它由两部分组成,一部分为一个int数组,另一部分为一个哈希表。
private void Initialize(int capacity)
{
int prime = HashHelpers.GetPrime(capacity);
this.buckets = new int[prime];
for (int i = 0; i < this.buckets.Length; i++)
{
this.buckets[i] = -1;
}
this.entries = new Entry<TKey, TValue>[prime];
this.freeList = -1;
}
有一个buckets整型数组,用来进行计算Hah碰撞。entries用来存储字典中的内容,并且标示下一个元素的位置。
这里涉及到一个分配字典长度的问题。不是说你在字典中添加一个元素,它的长度就添加1.在其内部,是以素数的方式增加。
类似:3,7,11,13,17.....这种,以达到均匀分配的目的。
比如当你的字典长度从7变成8时,实际上字典内部分配的空间是从7变成11,而不是8.
加入新的keyvalue时,首先进行buckets碰撞,这里使用的方法是除留余数法,如果发现buckets下标==-1,说明它没有被使用,这时将值存储到entries中。如果后进入的元素通过碰撞发现在buckets中已经占用,则会生成一个链表。新的entries指向上一个entries的下标。
当对字典进行删除操作时,同样会进行哈希碰撞,会在freelist里进行存储。后续的加入也是优先查看freelist中是否有剩余长度可用。
至于哈希表和字典的区别,有以下几点,只做概括:
[1] 单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分.
[2] 多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减.
[3] Dictionary 有按插入顺序排列数据的特性 (注: 但当调用 Remove() 删除过节点后顺序被打乱), 因此在需要体现顺序的情境中使用 Dictionary 能获得一定方便.
这里附上另一篇对于字典和哈希表的比较博文。