关于List和Array以及ArrayList应该都比较熟悉了,在本小节主要是对C#中的dictionary和hashtable的实现原理进行简单的分析。
一、hashtable的基本结构
1.一个hashtable有的字段:load fator装填因子(默认为0.72),capacity容量(初始为0),bucket桶的数量(初始为3)。
2.一个hashtable中所有的数据实际上是存储在bucket结构的数组中的,然后可以根据索引(哈希值)去访问桶中的数据。
二、在hashtable中增加元素的过程
Hashtable ht = new Hashtable(); ht.Add("a",100);
1.计算 已有数据的个数/桶的数量 值,如果该值小于装填因子:
(1)计算“a”的哈希值
(2)得到的哈希值对桶的数量取模得到位置
(3)在桶的该位置,将其key赋值为“a”,value赋值为100。
若哈希出现冲突,则使用开放定址法(线性哈希再散列)的方法 (HashOf(a)+di )% Array.length来寻找下一个位置放入数据。
2.计算 已有数据的个数/桶的数量 值,如果该值大于装填因子:
(1)增加桶的数量即扩充数组,扩充到最小两倍当前数组大小的素数(3变为7)
(2)将原桶数组中的所有key全部rehash一遍再放入新的数组中
(3)重复1中的步骤
三、在hashtable中查找元素的过程
var value = ht["a"];
1.计算“a”的哈希值
2.由该哈希值对桶数组取余得到所在的位置,并比较该位置所在的key是否为“a”,如果是返回其value否则进行开放地址寻找另一个位置,最后返回所在位置的value值
四、分析总结
1.Hashtable为什么速度查询速度快,而添加速度相对慢,且其添加和查询速度之比相差一个数量等级?
由以上分析可知,Hashtable查询速度近乎是O(1)的,而添加和删除操作是O(n),因为当容量不够时,要全部rehash一遍拷贝到新的数组。
2.装填因子( Load Factor)是什么,hashtable默认的装填因子是多少?
判断是否容量不足的指标是装填因子,当 已有数据的个数/桶的数量 大于装填因子时表示数量不够需要扩容。默认为0.72是微软官方给它制定的值,因为要考虑:当装填因子过大时,这样hash冲突会非常明显降低性能;当装填因子过小时,桶的数量太多,会造成内存的浪费。
3.hashtable里的元素是顺序排序的吗?
由计算过程可知,是无序的。
4.hashtable内部的数据桶(数组)默认长度多少,其长度为什么只能是素数?
默认长度是3。是素数的原因是素数的约数只有1和它本身,因而在容量对桶数组作取余操作的时候,得到hash值尽可能是唯一的。
以上分析基于.net framework4.5的源码,最近查看4.8源码似乎发现变化,hashtable和dictionary的实现原理相同了,有待进一步研究。