zoukankan      html  css  js  c++  java
  • hashtable

    Hashtable

               internal const Int32 HashPrime = 101;//一个固定的素数

            private const Int32 InitialSize = 3;//哈希表的默认容量

            private struct bucket {

                public Object key;//键

                public Object val;//值

                public int hash_col;//哈希码,最高位是符号位,0:正整数,表示未发生冲突,1:负整数,表示发生冲突,存储的是h(key, i)的值而不是哈希地址

            }

            private bucket[] buckets;

            private int count;//元素总数

            private int occupancy;//冲突次数

            private  int loadsize;//相当于一个阈值,达到了这个数值,将对哈希表进行扩容

            private  float loadFactor;//哈希表中的元素占有数据桶空间的一个比率,这个值直接决定了哈希表在什么时候进行扩容。

            private volatile int version;

            private volatile bool isWriterInProgress;  

            private ICollection keys;

            private ICollection values;

    在Hashtable中的两个哈希函数分别为:

    1.  h_1(k) = k.GetHashCode():第一个哈希函数直接用默认的GetHashCode()方法;

    2.  h_2(k) = (1 + ((h_1(k) * HashPrime) % (hashsize - 1))):HashPrime为私有成员101的素数,hashsize为哈希表长度。之所以会进行取模运算是为了保证结果值的范围在[0, hashsize - 1]。

    构建哈希算法的函数

    Hashtable解决冲突使用了双重散列法,但又跟前面所讲的双重散列法稍有不同。它探测地址的方法如下:

    h(key, i) = h1(key) + i * h2(key)

    其中哈希函数h1和h2的公式如下:

    h1(key) = key.GetHashCode()

    h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1))

    由于使用了二度哈希,最终的h(key, i)的值有可能会大于hashsize,所以需要对h(key, i)进行模运算,最终计算的哈希地址为:

    哈希地址 = h(key, i) % hashsize

    哈希表通过关键字查找元素时,首先计算出键的哈希地址,然后通过这个哈希地址直接访问数组的相应位置并对比两个键值,如果相同,则查找成功并返回;如果不同,则根据hash_coll的值来决定下一步操作。当hash_coll为0或正数时,表明没有冲突,此时查找失败;如果hash_coll为负数时,表明存在冲突,此时需通过二度哈希继续计算哈希地址进行查找,如此反复直到找到相应的键值表明查找成功,如果在查找过程中遇到hash_coll为正数或计算二度哈希的次数等于哈希表长度则查找失败。由此可知,将hash_coll的高位设为冲突位主要是为了提高查找速度,避免无意义地多次计算二度哈希的情况。

    一,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"的索引。

     

    1. Hashtable为什么查询速度快,而添加速度慢,且其添加和查询速度之比相差一个数量级?

    Hashtable查询速度快是因为内部是基于数组索引定位的,稍微消耗性能的是取key’的哈希值,添加性能慢是因为添加元素可能会地址冲突,需要重新定位地址,扩容后数组拷贝,重新计算key。

    1. 装填因子(loadfactor)是什么,hashtable默认的装填因子时多少?

    装填因子是hashtable中已存元素数/内部bucket数组长度,这个比值太大会造成冲突概率增大,太小会造成空间浪费,默认是0.72.

    1. Hashtable里的元素是顺序排序的吗?

    不是顺序的

    1. Hashtable内部的数据桶默认长度是多少,其长度为什么只能是素数?

    默认长度是3,因为素数有一个特点,只能被自己和1整除,如果不是素数,那么取模运算时可能会出现多个值。

  • 相关阅读:
    Linux软件安装【JDK安装】
    接口自动化<009>:Python 接口自动化Mock使用
    接口自动化<008>:Python 自定义装饰器
    接口自动化<007>:Python 装饰器 @functools.wraps(func)
    接口自动化<006>:Python 装饰器 @retry
    接口自动化<005>:Python中*args 和 **kwargs的用法详解
    接口自动化<004>:json.dumps()、json.loads()与json.dump()、json.load()
    接口自动化<003>:request部分参数解析
    pycharm常用设置及快捷键说明
    接口自动化<002>:session.request 会话用作前后文管理器
  • 原文地址:https://www.cnblogs.com/mcyushao/p/10652291.html
Copyright © 2011-2022 走看看