C#容器类的原理解析(一)

关于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的实现原理相同了,有待进一步研究。

C#容器类的原理解析(一)