【C# 集合】HashTable .net core 中的Hashtable的实现原理
上一篇我介绍了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的实现原理的更多相关文章
- Java中的集合(十三) 实现Map接口的Hashtable
Java中的集合(十三) 实现Map接口的Hashtable 一.Hashtable简介 和HashMap一样,Hashtable采用“拉链法”实现一个哈希表,它存储的内容是键值对(key-value ...
- 特殊集合(stack、queue、hashtable的示例及练习)
特殊集合:stack,queue,hashtable stack:先进后出,一个一个的赋值一个一个的取值,按照顺序. .count 取集合内元素的个数 .push() ...
- HashTable和HashSet中的类型陷阱
HashTable和HashSet中的类型陷阱 发现这个陷阱的起因是这样的:我现在有上百万字符串,我准备用TopK算法统计出出现次数做多的前100个字符串. 首先我用Hashtable统计出了每个字符 ...
- java集合框架(二):HashTable
HashTable作为集合框架中的一员,现在是很少使用了,一般都是在面试中会问到其与HashMap的区别.为了能在求职的时候用上场,我们有必要对其原理进行解读. HashTable的实现原理跟Hash ...
- java集合框架collection(4)HashMap和Hashtable的区别
HashMap和Hashtable的区别 HashMap和Hashtable都实现了Map接口,但决定用哪一个之前先要弄清楚它们之间的分别.主要的区别有:线程安全性,同步(synchronizatio ...
- Java集合系列(四):HashMap、Hashtable、LinkedHashMap、TreeMap的使用方法及区别
本篇博客主要讲解Map接口的4个实现类HashMap.Hashtable.LinkedHashMap.TreeMap的使用方法以及三者之间的区别. 注意:本文中代码使用的JDK版本为1.8.0_191 ...
- Java中的HashTable详解
Hashtables提供了一个很有用的方法可以使应用程序的性能达到最佳. Hashtables(哈 希表)在计算机领域中已不 是一个新概念了.它们是用来加快计算机的处理速度的,用当今的标准来处理,速度 ...
- 在webservice中传递Hashtable
webservice中不支持hashtable的数据类型,那么如何在webservice中传递hashtable呢?我们可以通过将hashtable转化为webservice中支持的数组的类型来进行传 ...
- EF Core中如何通过实体集合属性删除从表的数据
假设在数据库中有两个表:Person表和Book表,Person和Book是一对多关系 Person表数据: Book表数据: 可以看到数据库Book表中所有的数据都属于Person表中"F ...
随机推荐
- 关于启动bash提示‘bash: export: `//这是新的': not a valid identifier’的解决办法
学习linux以来将centos改的也不少了,也不知道这个问题是由于那个修改来的.最近改bash的操作环境配置文件,用到了~/.bashrc这个文件,发现里面被我修改过. 那是当年安装fcitx输入法 ...
- P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two
// Problem: P1518 [USACO2.4]两只塔姆沃斯牛 The Tamworth Two // Contest: Luogu // URL: https://www.luogu.com ...
- 社交网络分析的 R 基础:(一)初探 R 语言
写在前面 3 年的硕士生涯一转眼就过去了,和社交网络也打了很长时间交道.最近突然想给自己挖个坑,想给这 3 年写个总结,画上一个句号.回想当时学习 R 语言时也是非常戏剧性的,开始科研生活时到处发邮件 ...
- ABC182 F Valid payments
解法一 首先不妨来思考一下怎样的一个付钱方案是最优的,假设需要支付 \(Y\) 元,第 \(a_i\) 种钱币支付了 \(s_i\) 张,那么必须有:\(s_i < \frac{a_{i + 1 ...
- Github上点赞90k的计算机基础、操作系统、网络笔记,赶紧收藏
最近,有粉丝发消息给我,说想要好好学习计算机,但是找不到门路啊,所以小编打算给大家推荐一些很奈斯的计算机学习的资料,希望大家赶紧收藏起来好好学习. 资料分为四份,给大家展示了部分内容,头条受限,无法全 ...
- 微信、QQ添加Universal Link
官方说明 1. Universal Links是什么 Universal Links(通用链接),是苹果在iOS9之后,提供的一种"可通过传统HTTP链接来跳转APP"的能力. 该 ...
- Java中的增强型for循环
下面是关于增强型for循环对一维数组与二维数组遍历的具体实现: public class NewForLoop { public static void main(String[] args) { i ...
- 友盟分享,极光推送Demo
友盟分享SDK下载及官方文档 http://dev.umeng.com/social/ios/detail-share 1.注册应用获取App友盟Appkey(556d14ad67e58eb08400 ...
- 利用redis+AOP简单处理MQ冥等问题
思路: 1.利用redis内部的串行执行特性,使用getandset()处理分布式问题; 2.注解提供入参选择,通过数据抽取后计算MD5值,实现业务性值的冥等: 代码区: 1.注解 1 /** 2 * ...
- Java中ArrayList边遍历边修改
用for-each 边遍历ArrayList 边修改时: public static void main(String[] args) { ArrayList<String> list = ...