zoukankan      html  css  js  c++  java
  • 从内部剖析C#集合之HashTable

      计划写几篇文章专门介绍HashTable,Dictionary,HashSet,SortedList,List 等集合对象,从内部剖析原理,以便在实际应用中有针对性的选择使用。 这篇文章先介绍HashTable .
      先例举几个问题:1,Hashtable为什么速度查询速度快,而添加速度相对慢,且其添加和查询速度之比相差一个数量等级?
      2,装填因子( Load Factor)是什么,hashtable默认的装填因子是多少?
      3,hashtable里的元素是顺序排序的吗?
      4,hashtable内部的数据桶(数组)默认长度多少,其长度为什么只能是素数?
      Hashtable中的数据实际存储在内部的一个数据桶里(bucket结构数组),其和普通的数组一样,容量固定,根据数组索引获取值。
      下面从正常使用Hashtable场景看内部是如何实现的,内部都做了哪些工作。
      一,new一个Hashtable,Hashtable ht=new Hashtable();
      Hashtable有多个构造函数,常用的是无参构造函数:Hashtable ht=new Hashtable(),在new一个hashtable时,其内部做了如下工作:调用Hashtable(int capacity,float loadFactor),其中capacity为:0,loadFactor为:1,然后初始化bocket数组大小为3,装载因子为0.72(该值是微软权衡后给的值),如下图所示,该图截取Reflector
      二,向Hashtable添加一个元素,ht.Add("a","123")
      1,判断当前Hashtable :ht的元素个数与bucket数组之比是否超过装载因子0.72,
      1)小于0.72:对a进行哈希取值,然后将得到的值与bucket数组长度进行取模计算,将取模的结果插入到bucket数组对应索引,将"123"赋值其value.
      因为哈希值可能会重复(不明白的百度一下),从而导致地址冲突,Hashtable 采用的是 "开放定址法" 处理冲突, 具体行为是把 HashOf(k) % Array.Length 改为 (HashOf(k) + d(k)) % Array.Length , 得出另外一个位置来存储关键字 "a" 所对应的数据, d 是一个增量函数。 如果仍然冲突, 则再次进行增量, 依此循环直到找到一个 Array 中的空位为止。
      2) 大于0.72:对bucket数组进行扩容,a, 新建一个数组(该数组的大小为两倍于当前容量最小的素数,比如当前数组长度是3,那么新数组长度为7)。
      b,将原来数组元素拷贝到新的数组中,因为bocket数组长度变了,所以需要对所有key重新哈希计算(这是影响hashtable性能的重要因素)。
      c, 进行上面a步骤。
      三,通过key获取Hashtable对应的value,var v=ht["a"];
      1) 计算"a"的哈希值。
      2)将计算的结果与bocket数组长度进行取模计算,因为哈希值可能会冲突,所以类似定位索引上的key可能与输入的key不相同,这时继续查找下一个位置……
      3)取模计算结果即是存储在bocket数组上"123"的索引。
      Hashtable还有很多方法,比如Clear ,Remove ,ContainsKey,ContainsValue等方法,因篇幅有限这里就不一一介绍了。
      写到这里来回答一下篇幅开头的几个问题。
      1,Hashtable查询速度快是因为内部是基于数组索引定位的,稍微消耗性能的是取KEY的哈希值,添加性能相对查询慢是因为:a,添加元素时可能会地址冲突,需要重新定位地址 . b,扩容后 数组拷贝,重新哈希计算旧数组所有key托福答案
      2, 装填因子是Hashtable"已存元素个数/内部bucket数组长度",这个比值太大会造成冲突概率增大,太小会造成空间的浪费。默认是0.72,该值是微软经过大量实验得出的一个比较平衡的值,装填因子范围 0.1<loadFactor<1,否则抛出ArgumentOutOfRangeException异常。
      3,不是顺序的(各位看了文章应该知道为什么不是顺序的了吧?)
      4,默认长度是3,我看的是。net framework 4.5版本反编译的代码,其他版本的。net framework不确定是不是这个值。为什么扩容的数组长度一定要是素数呢?因为素数有一个特点,只能被自己和1整除,如果不是素数那么在进行取模计算的时候可能会出现多个值。

  • 相关阅读:
    对话框中获取Month Calendar的当前日期
    JAVA GUI程序示例
    MFC控件显示值和控件变量值的更新
    JAVA求10到99的随机数
    学习之路二:关于集合和数组内在联系的深入了解
    迭代器学习之三:IEnumerable和IEnumerator的泛型结构
    迭代器学习之四:关于yield的深入了解
    迭代器学习之二:数组的可枚举类型和枚举数的定义以及编译器的foreach工作原理
    学习之路三:关于运用单线程和委托以及事件自定义Timer类
    迭代器学习之一:使用IEnumerable和IEnumerator接口
  • 原文地址:https://www.cnblogs.com/haosola/p/3724505.html
Copyright © 2011-2022 走看看