C#Dictionary源码
Dictionary
Dictionary与hashtable的区别:dictionary支持泛型。
通常处理哈希冲突的方法有:开放地址法,再哈希法,链地址法,建立一个公共栈区等。
在哈希表上进行查找的过程和哈希造表的过程基本一致。给定k值,根据造表时设定的哈希函数求得哈希地址,若表中此位置没有记录,则查找不成功;否则比较关键字,若和给定值相等,则查找成功;否则根据冲突的方法寻找下一地址,直到哈希表中某个位置为空或者表中所填关键值等于给定值为止。
Dictionary使用的哈希函数是除留余数法 h = F(k) % m;m为哈希表长度。
Dictionary使用的解决冲突的方法是拉链法,又称链地址法
拉链法的原理:将所有关键字为同义词的结点链接在同一个单链表中。若选定的散列表长度为m,则可将散列表定义为一个由m个头指针组成的指针数 组T[0..m-1]。凡是散列地址为i的结点,均插入到以T[i]为头指针的单链表中。T中各分量的初值均应为空指针。
private struct Entry {
public int hashCode; //31位散列值,32最高位表示符号位,-1表示未使用
public int next; //下一项的索引值,-1表示结尾
public TKey key; //键
public TValue value; //值
}
private int[] buckets;//内部维护的数据地址
private Entry[] entries;//元素数组,用于维护哈希表中的数据
private int count;//元素数量
private int version;
private int freeList;//空闲的列表
private int freeCount;//空闲列表元素数量
private IEqualityComparer<TKey> comparer;//哈希表中的比较函数
private KeyCollection keys;//键集合
private ValueCollection values;//值集合
private Object _syncRoot;
初始化函数
该函数用于,初始化的数据构造
private void Initialize(int capacity) {
//根据构造函数设定的初始容量,获取一个近似的素数
int size = HashHelpers.GetPrime(capacity);
buckets = new int[size];
for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;
entries = new Entry[size];
freeList = -1;
}
size 哈希表的长度是素数,可以使元素更均匀地分布在每个节点上。GetPrime(capacity)返回离>capacity最近的质数。维护了个质数数组,初始capacity为0,返回3(初始大小)
buckets 中的节点值,-1表示空值。
freeList 为-1表示没有空链表。
buckets 和 freeList 所值指向的数据其实全是存储于一块连续的内存空间(entries )之中。
//取hashcode后还与0x7FFFFFFF做了个与操作,0x7FFFFFFF这就是int32.MaxValue的16进制,换成二进制是01111111111111111111111111111111,第1位是符号位,也就是说comparer.GetHashCode(key) 为正数的情况下与0x7FFFFFFF做 & 操作结果还是它本身,如果取到的hashcode是负数,负数的二进制是取反再补码,所以结果得到的是0x7FFFFFFF-(-hashcode)+1,结果是正数。其实简单来说,它的目的就是高性能的取正数。
扩容 //Resize消耗不低,比List<T>的要大,不光要copy元素,还要重建bucket。
private void Resize() {
Resize(HashHelpers.ExpandPrime(count), false);
}
private void Resize(int newSize, bool forceNewHashCodes) {
Contract.Assert(newSize >= entries.Length);
//重新初始化一个比原来空间还要大2倍左右的buckets和Entries,用于接收原来的buckets和Entries的数据
int[] newBuckets = new int[newSize];
for (int i = 0; i < newBuckets.Length; i++) newBuckets[i] = -1;
Entry[] newEntries = new Entry[newSize];
//数据搬家
Array.Copy(entries, 0, newEntries, 0, count);
//将散列值刷新,这是在某一个单链表节点数到达一个阈值(100)时触发
if(forceNewHashCodes) {
for (int i = 0; i < count; i++) {
if(newEntries[i].hashCode != -1) {
newEntries[i].hashCode = (comparer.GetHashCode(newEntries[i].key) & 0x7FFFFFFF);
}
}
}
//单链表数据对齐,无关顺序
for (int i = 0; i < count; i++) {
if (newEntries[i].hashCode >= 0) {
int bucket = newEntries[i].hashCode % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
buckets = newBuckets;
entries = newEntries;
}
Dictionary为了性能并没有在Remove做重建,而是把位置空出来,这样节省大量时间。freeList和bucket类似(一样喜新厌旧),总是指向最新空出来的entry的index,而entry的next又把所有空的entry连起来了。这样insert时就可以先找到这些空填进去。
C#Dictionary源码的更多相关文章
- C# Dictionary源码剖析---哈希处理冲突的方法有:开放定址法、再哈希法、链地址法、建立一个公共溢出区等
C# Dictionary源码剖析 参考:https://blog.csdn.net/exiaojiu/article/details/51252515 http://www.cnblogs.com/ ...
- java.util.Dictionary源码分析
Dictionary是一个抽象类,Hashtable是它的一个子类. 类的声明:/** The <code>Dictionary</code> class is the abs ...
- C# Dictionary源码剖析
参考:https://blog.csdn.net/exiaojiu/article/details/51252515 http://www.cnblogs.com/wangjun1234/p/3719 ...
- 从Dictionary源码看哈希表
一.基本概念 哈希:哈希是一种查找算法,在关键字和元素的存储地址之间建立一个确定的对应关系,每个关键字对应唯一的存储地址,这些存储地址构成了有限.连续的存储地址. 哈希函数:在关键字和元素的存储地址之 ...
- .net源码分析 - ConcurrentDictionary<TKey, TValue>
List源码分析 Dictionary源码分析 ConcurrentDictionary源码分析 继上篇Dictionary源码分析,上篇讲过的在这里不会再重复 ConcurrentDictionar ...
- Java 容器源码分析之Map-Set-List
HashMap 的实现原理 HashMap 概述 HashMap 是基于哈希表的 Map 接口的非同步实现.此实现提供所有可选的映射操作,并允许使用 null 值和 null 键.此类不保证映射的顺序 ...
- Java基础——HashTable源码分析
HashTable是基于哈希表的Map接口的同步实现 HashTable中元素的key是唯一的,value值可重复 HashTable中元素的key和value不允许为null,如果遇到null,则返 ...
- .net源码分析 – Dictionary<TKey, TValue>
接上篇:.net源码分析 – List<T> Dictionary<TKey, TValue>源码地址:https://github.com/dotnet/corefx/blo ...
- 源码分析之Dictionary笔记
接下来我们一步步来熟悉 Dictionary的底层结构实现,下面的MyDictionary等同于源码中的Dictionary看待. 首先我们定义一个类 MyDictionary,类中定义一个结构Ent ...
随机推荐
- Zookeeper集群节点数量为什么要是奇数个?
无论是公司的生产环境,还是自己搭建的测试环境,Zookeeper集群的节点个数都是奇数个.至于为什么要是奇数个,以前只是模糊的知道是为了满足选举需要,并不知道详细的原因.最近重点学习zookeeper ...
- ubuntu18.04 安装mysql server
mysql 5.7支持的最高版本是Ubuntu17 ,即使安装成功后,也会出现各种妖蛾子,本人就被这种问题困扰了好一会.在Ubuntu 18.04下安装mysql,建议安装8.0以上版本! 1. 配置 ...
- EL(Expression Language)和JSTL标签(JSP Standard Tag Library)
一.EL表达式: Expression Language提供了在 JSP 脚本编制元素范围外(例如:脚本标签)使用运行时表达式的功能.脚本编制元素是指页面中能够用于在JSP 文件中嵌入 Java代码的 ...
- node.js打印function
var Person = function(name) { this.name = name; this.gender = ['man', 'woman']; } console.log(Person ...
- JVM内容梳理
- leetcode每日刷题计划-简单篇day1
orzorz开始刷题 争取坚持每周平均下来简单题一天能做两道题吧 非常简单的题奇奇怪怪的错误orz越不做越菜 Num 7 整数反转 Reverse Integer 刚开始多给了一个变量来回折腾占地方, ...
- SpringBoot 之 MVC
SpringBoot MVC 和静态资源 首先,我们一定要搞清楚,mvc 配置和 static 配置的联系和区别. mvc 配置其实就是给 spring mvc 框架用的, 具体来说, 比如 @Req ...
- VS2017打包设置
本文为网络贴文,引用于:http://www.cnblogs.com/overstep/p/6942423.html 一. 安装打包插件: 安装打包插件:Microsoft Visual Studi ...
- centos7-内核版本降级
1. 查看内核版本参考命令: [root@localhost ~]# cat /etc/redhat-release CentOS Linux release 7.3.1611 (Core) [roo ...
- “AS3.0高级动画编程”学习:第四章 寻路(AStar/A星/A*)算法 (下)
在前一部分的最后,我们给出了一个寻路的示例,在大多数情况下,运行还算良好,但是有一个小问题,如下图: 很明显,障碍物已经把路堵死了,但是小球仍然穿过对角线跑了出来! 问题在哪里:我们先回顾一下ASta ...