上一篇我介绍了Hash函数

这篇我来说一下Hash函数在 HashTable中的应用。

HashTable的特性:

1、装载因子:.net core 0.72 ,java 0.75

2、冲突解决方案:Hashtable使用了闭散列法来解决冲突,java采用 开散列法解决冲突.

3、bucket(桶):用来转载hash函数的返回值和建立key和hash返回值的关系

4、Hash函数以及算法

5、HashTable是可以序列化的。是线程安全的。HashTable之所以是线程安全的,是因为方法上都加了synchronized关键字。

6、可枚举

7、在新的开发中不建议使用HashTable,建议使用Dictionary<Tkey,Tvalue>ConcurrencyDictionary<Tkey,Tvalue>

.net core 中的Hashtable的实现原理

它通过一个结构体bucket来表示哈希表中的单个元素,这个结构体中有三个成员:

private struct Bucket
{
public object? key;
public object? val;
public int hash_coll; // bucket结构体的hash_coll字段所存储的是h(key, i)的值而不是哈希地址
}

(1)       key :表示键,即哈希表中的关键字。
(2)       val :表示值,即跟关键字所对应值。
(3)       hash_coll :它是一个int类型,用于表示键所对应的哈希码,.net core bucket结构体的hash_coll字段所存储的是h(key, i)的值而不是哈希地址 。

int类型占据32个位的存储空间,它的最高位是符号位,为“0”时,表示这是一个正整数;为“1”时表示负整数。hash_coll使用最高位表示当前位置是否发生冲突,为“0”时,也就是为正数时,表示未发生冲突;为“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

【注意】:bucket结构体的hash_coll字段所存储的是h(key, i)的值而不是哈希地址。

哈希表的所有元素存放于一个名称为buckets(又称为数据桶) 的bucket数组之中,下面演示一个哈希表的数据的插入和删除过程,其中数据元素使用(键,值,哈希码)来表示。注意,本例假设Hashtable的长度为11,即hashsize = 11,这里只显示其中的前5个元素。

(1)插入元素(k1,v1,1)和(k2,v2,2)。

由于插入的两个元素不存在冲突,所以直接使用h1(key) % hashsize的值做为其哈希码而忽略了h2(key)。其效果如图8.6所示。

(2)      插入元素(k3,v3,12)

新插入的元素的哈希码为12,由于哈希表长为11,12 % 11 = 1,所以新元素应该插入到索引1处,但由于索引1处已经被k1占据,所以需要使用h2(key)重新计算哈希码。

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

h2(key) = 1 + ((12 >> 5) + 1) % (11 - 1)) = 2

新的哈希地址为 h1(key) + i * h2(key) = 1 + 1 * 2 = 3,所以k3插入到索引3处。而由于索引1处存在冲突,所以需要置其最高位为“1”。

(10000000000000000000000000000001)2 = (-2147483647)10

最终效果如图8.7所示。

(3)       插入元素(k4,v4,14)

k4的哈希码为14,14 % 11 = 3,而索引3处已被k3占据,所以使用二度哈希重新计算地址,得到新地址为14。索引3处存在冲突,所以需要置高位为“1”。

(12)10 = (00000000000000000000000000001100)2   高位置“1”后

(10000000000000000000000000001100)2 = (-2147483636)10

最终效果如图8.8所示。

(4)       删除元素k1和k2

Hashtable在删除一个存在冲突的元素时(hash_coll为负数),会把这个元素的key指向数组buckets,同时将该元素的hash_coll的低31位全部置“0”而保留最高位,由于原hash_coll为负数,所以最高位为“1”。

(10000000000000000000000000000000)2 = (-2147483648)10

单凭判断hash_coll的值是否为-2147483648无法判断某个索引处是否为空,因为当索引0处存在冲突时,它的hash_coll的值同样也为-2147483648,这也是为什么要把key指向buckets的原因。这里把key指向buckets并且hash_coll值为-2147483648的空位称为“有冲突空位”。如图8.8所示,当k1被删除后,索引1处的空位就是有冲突空位。

Hashtable在删除一个不存在冲突的元素时(hash_coll为正数),会把键和值都设为null,hash_coll的值设为0。这种没有冲突的空位称为“无冲突空位”,如图8.9所示,k2被删除后索引2处就属于无冲突空位,当一个Hashtable被初始化后,buckets数组中的所有位置都是无冲突空位。

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

【C# 集合】HashTable .net core 中的Hashtable的实现原理的更多相关文章

  1. Java中的集合(十三) 实现Map接口的Hashtable

    Java中的集合(十三) 实现Map接口的Hashtable 一.Hashtable简介 和HashMap一样,Hashtable采用“拉链法”实现一个哈希表,它存储的内容是键值对(key-value ...

  2. 特殊集合(stack、queue、hashtable的示例及练习)

    特殊集合:stack,queue,hashtable stack:先进后出,一个一个的赋值一个一个的取值,按照顺序. .count           取集合内元素的个数 .push()        ...

  3. HashTable和HashSet中的类型陷阱

    HashTable和HashSet中的类型陷阱 发现这个陷阱的起因是这样的:我现在有上百万字符串,我准备用TopK算法统计出出现次数做多的前100个字符串. 首先我用Hashtable统计出了每个字符 ...

  4. java集合框架(二):HashTable

    HashTable作为集合框架中的一员,现在是很少使用了,一般都是在面试中会问到其与HashMap的区别.为了能在求职的时候用上场,我们有必要对其原理进行解读. HashTable的实现原理跟Hash ...

  5. java集合框架collection(4)HashMap和Hashtable的区别

    HashMap和Hashtable的区别 HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别.主要的区别有:线程安全性,同步(synchronizatio ...

  6. Java集合系列(四):HashMap、Hashtable、LinkedHashMap、TreeMap的使用方法及区别

    本篇博客主要讲解Map接口的4个实现类HashMap.Hashtable.LinkedHashMap.TreeMap的使用方法以及三者之间的区别. 注意:本文中代码使用的JDK版本为1.8.0_191 ...

  7. Java中的HashTable详解

    Hashtables提供了一个很有用的方法可以使应用程序的性能达到最佳. Hashtables(哈 希表)在计算机领域中已不 是一个新概念了.它们是用来加快计算机的处理速度的,用当今的标准来处理,速度 ...

  8. 在webservice中传递Hashtable

    webservice中不支持hashtable的数据类型,那么如何在webservice中传递hashtable呢?我们可以通过将hashtable转化为webservice中支持的数组的类型来进行传 ...

  9. EF Core中如何通过实体集合属性删除从表的数据

    假设在数据库中有两个表:Person表和Book表,Person和Book是一对多关系 Person表数据: Book表数据: 可以看到数据库Book表中所有的数据都属于Person表中"F ...

随机推荐

  1. 猫与ThinkPad

    高中时候看见过家里橘猫谁在舅舅的ThinkPad笔记本了,可惜没拍下来,我也不喜欢那只猫,更喜欢幼时的白猫和黑白猫. ThinkPad宣传图片诚不欺我. 怀念青春与当年陪我游戏的IBM的ThinkPa ...

  2. Cesium入门13 - Extras - 附加内容

    Cesium入门13 - Extras - 附加内容 Cesium中文网:http://cesiumcn.org/ | 国内快速访问:http://cesium.coinidea.com/ 剩下的代码 ...

  3. 2022GDUT寒训专题一C题

    题目 题面 马在中国象棋以日字形规则移动. 请编写一段程序,给定n×m大小的棋盘,以及马的初始位置 (x, y),要求不能重复经过棋盘上的同一个点,计算马可以有多少途径遍历棋盘上的所有点. 输入格式 ...

  4. CVE-2021-26119 PHP Smarty 模版沙箱逃逸远程代码执行漏洞

    0x00 漏洞介绍 smarty是一个基于PHP开发的PHP模板引擎.它提供了逻辑与外在内容的分离,简单的讲,目的就是要使用PHP程序员同美工分离,使用的程序员改变程序的逻辑内容不会影响到美工的页面设 ...

  5. gin的源码解读4-gin的路由算法

    gin的路由算法 gin的是路由算法其实就是一个Trie树(也就是前缀树). 有关数据结构的可以自己去网上找相关资料查看. 注册路由预处理 我们在使用gin时通过下面的代码注册路由 普通注册 rout ...

  6. ORA-15081: failed to submit an I/O operation to a disk

    Problem: While restoring controlfile to test environment, from filesystem or tape environment after ...

  7. python32day

    内容回顾 操作系统的历史 多道操作系统 分时操作系统 实时操作系统 进程 线程 并行和并发 同步和异步 阻塞和非阻塞 今日内容 进程的三状态图 进程的调度算法 给所有进程分配资源或者分配CPU使用权的 ...

  8. Vue3源码分析之Diff算法

    Diff 算法源码(结合源码写的简易版本) 备注:文章后面有详细解析,先简单浏览一遍整体代码,更容易阅读 // Vue3 中的 diff 算法 // 模拟节点 const { oldVirtualDo ...

  9. linux 编译C++

    转载请注明来源:https://www.cnblogs.com/hookjc/ makefile文件内容: main:main.o fun1.o fun2.o g++ -o main  main.o ...

  10. tcp协议下的Socket

    import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.net ...