通过.net core源码看下Dictionary的实现
.net core的代码位置
C#中,Dictionary这个数据结构并不是很容易理解,因为看上不去并不像C++的map。底层是如何实现一个字典的并完全可知,因为从数据结构来说,很多结构都可以支持一个类似的加速key-value对存储的访问形式。比如tree,跳表,hashtable等等。
基于bucket的Hashtable
Dictionary的基本思想是通过一个Entry数值存储数据(key和value),其中的数据是紧密排布的。然后,通过bucket数组实现hashcode加速查找。如果两个对象的hashcode%length(数值的长度)相等,实现类似hashtable碰撞的退避规则,并通过Entry.next的引用住新的退避位置(用数组下标实现连接)。
private struct Entry
{
public int hashCode; // Lower 31 bits of hash code, -1 if unused
public int next; // Index of next entry, -1 if last
public TKey key; // Key of entry
public TValue value; // Value of entry
}
private int[] _buckets;
private Entry[] _entries;
if (last < )
{
// Value in buckets is 1-based
buckets[bucket] = entry.next + ;
}
else
{
entries[last].next = entry.next;
}
关于Keys和Values
- private KeyCollection _keys;
- private ValueCollection _values;
许多时候,我们会用到对Keys和Values的访问。那我们来看看,这两个属性是如何实现的。先看一下KeyCollection的实现。这里删除了一些多余的代码,可以看出,他仅仅对dict的一个组合关系,内部的实际工作者是dict。
public sealed class KeyCollection : ICollection<TKey>, ICollection, IReadOnlyCollection<TKey>
{
private Dictionary<TKey, TValue> _dictionary;
public KeyCollection(Dictionary<TKey, TValue> dictionary)
{
_dictionary = dictionary;
}
void ICollection<TKey>.Add(TKey item)
=> ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
void ICollection<TKey>.Clear()
=> ThrowHelper.ThrowNotSupportedException(ExceptionResource.NotSupported_KeyCollectionSet);
bool ICollection<TKey>.Contains(TKey item)
=> _dictionary.ContainsKey(item);
}
然后,看一下迭代过程的实现。非常简单,仅仅是每次都把_currentKey赋值为_entries的下一个元素。所以,可以看出来,Keys的访问是有序的(按插入顺序)。
public bool MoveNext()
{
while ((uint)_index < (uint)_dictionary._count)
{
ref Entry entry = ref _dictionary._entries[_index++];
if (entry.hashCode >= )
{
_currentKey = entry.key;
return true;
}
}
_index = _dictionary._count + ;
_currentKey = default;
return false;
}
values和keys的实现是完全一致的,所以Values的访问和Keys的访问性能是差不多的,不存在访问Keys快,访问Values慢的情况。
关于空间大小算法
大家知道hash表是需要先分配一块比较大的空间,并在保持一定数据密度的情况下,会拥有比较高的存储和访问效率。
C#的dict,永远会去找当前需求的capacity的下一个素数,作为数组的分配size。如果,默认new Dict,传递的capacity是0,那么实际此时的_entries大小是3。
找素数的逻辑稍微提下。会先顺序遍历存储的primes数组;如果找不到,再用逐个数字遍历的方式找接下来的素数。
public static readonly int[] primes = {
, , , , , , , , , , , , , , , , , , , , , , ,
, , , , , , , , , , , , , , ,
, , , , , , , , , , , , ,
, , , , , , , , , , , ,
, , , , , , , , };
关于读取数据的效率
题外话,讲一下有的同学喜欢这么写数据访问的代码。
if (techAddonDict.ContainsKey())
{
var c = techAddonDict[];
}
从底层来说,所有查找的代码,都会先通过bucket找到一次entry对象(通过FindEntry函数)。那么上一段函数中实际需要访问两次FindEntry函数。
float v;
//todo xxx
}
这段函数就很明显了,只需要访问一次FindEntry函数,性能自然会好一倍。
通过.net core源码看下Dictionary的实现的更多相关文章
- 一起来看CORE源码(一) ConcurrentDictionary
先贴源码地址 https://github.com/dotnet/corefx/blob/master/src/System.Collections.Concurrent/src/System/Col ...
- ASP.NET Core[源码分析篇] - Authentication认证
原文:ASP.NET Core[源码分析篇] - Authentication认证 追本溯源,从使用开始 首先看一下我们通常是如何使用微软自带的认证,一般在Startup里面配置我们所需的依赖认证服务 ...
- 从源码看JDK提供的线程池(ThreadPoolExecutor)
一丶什么是线程池 (1)博主在听到线程池三个字的时候第一个想法就是数据库连接池,回忆一下,我们在学JavaWeb的时候怎么理解数据库连接池的,数据库创建连接和关闭连接是一个比较耗费资源的事情,对于那些 ...
- 一个由正则表达式引发的血案 vs2017使用rdlc实现批量打印 vs2017使用rdlc [asp.net core 源码分析] 01 - Session SignalR sql for xml path用法 MemCahe C# 操作Excel图形——绘制、读取、隐藏、删除图形 IOC,DIP,DI,IoC容器
1. 血案由来 近期我在为Lazada卖家中心做一个自助注册的项目,其中的shop name校验规则较为复杂,要求:1. 英文字母大小写2. 数字3. 越南文4. 一些特殊字符,如“&”,“- ...
- 源码分析之Dictionary笔记
接下来我们一步步来熟悉 Dictionary的底层结构实现,下面的MyDictionary等同于源码中的Dictionary看待. 首先我们定义一个类 MyDictionary,类中定义一个结构Ent ...
- 从微信小程序开发者工具源码看实现原理(一)- - 小程序架构设计
使用微信小程序开发已经很长时间了,对小程序开发已经相当熟练了:但是作为一名对技术有追求的前端开发,仅仅熟练掌握小程序的开发感觉还是不够的,我们应该更进一步的去理解其背后实现的原理以及对应的考量,这可能 ...
- DOTNET CORE源码分析之IOC容器结果获取内容补充
补充一下ServiceProvider的内容 可能上一篇文章DOTNET CORE源码分析之IServiceProvider.ServiceProvider.IServiceProviderEngin ...
- 从Linux源码看Socket(TCP)的listen及连接队列
从Linux源码看Socket(TCP)的listen及连接队列 前言 笔者一直觉得如果能知道从应用到框架再到操作系统的每一处代码,是一件Exciting的事情. 今天笔者就来从Linux源码的角度看 ...
- 从源码看Azkaban作业流下发过程
上一篇零散地罗列了看源码时记录的一些类的信息,这篇完整介绍一个作业流在Azkaban中的执行过程,希望可以帮助刚刚接手Azkaban相关工作的开发.测试. 一.Azkaban简介 Azkaban作为开 ...
随机推荐
- global作用域
1 global在函数内部 $somevar=15; function addit () { GLOBAL $somevar; $somevar++ ; echo "somevar is ...
- wpf仿qq边缘自动停靠,支持多屏
wpf完全模仿qq边缘自动隐藏功能,采用鼠标钩子获取鼠标当前状态,在通过当前鼠标的位置和点击状态来计算是否需要隐藏. 以下是实现的具体方法: 一.鼠标钩子实时获取当前鼠标的位置和点击状态 /// &l ...
- python3-file文件操作
# Auther: Aaron Fan '''打开文件的模式有三种:r,只读模式(默认).w,只写模式.[不可读:不存在则创建:存在则删除内容:因为会清空原有文件的内容,一定要慎用]a,追加模式.[可 ...
- 前端(HTML/CSS/JS)-CSS编码规范
1. 文件名规范 文件名建议用小写字母加中横线的方式.为什么呢?因为这样可读性比较强,看起来比较清爽 https://stackoverflow.com/questions/25704650/disa ...
- java全栈day01-03注释、关键字与标识符
通常我们需要在源代码中添加文字用来对进行代码解释说明,但这些文字并不是Java代码的语法,会导致编译出错.这时我们可以使用注释来完成这一事项! 在编译时,编译器会忽略注释的存在,就好像注释内容不存在一 ...
- Siemens3508手机AT指令发送短信的实验
凡夫 最近利用Siemens3508旧手机做了AT指令发送短信的实验.有人可能认为我费那么大劲折腾累不累,告诉你这可是废物再利用,可以利用旧手机里的GSM/GPRS模块做无线远程多点分布数据采集.监控 ...
- Android二维码扫描功能的集成开发
二维码开发主要依赖ZXing开源项目 项目地址:https://github.com/zxing/zxing 这个开源项目可以扫描一维,和二维码, 一维码指的是书后面的条形码 首先配置ZXing库和A ...
- Altium designer的PCB设计规则
PCB布线规则,布板需要注意的点很多,但是基本上注意到了下面的这此规则,LAYOUT PCB应该会比较好,不管是高速还是低频电路,都基本如此. 1. 一般规则 1.1 PCB板上预划分数字.模拟.DA ...
- HTML5与CSS3设计模式 中文版 高清PDF扫描版
HTML5与CSS3设计模式是一部全面讲述用HTML5和CSS3设计网页的教程.书中含350个即时可用的模式 (HTML5和CSS3代码片段),直接复制粘贴即可使用,更可以组合起来构建出无穷的解决方案 ...
- C# Linq及Lamda表达式实战应用之 GroupBy 分组统计
在项目中做统计图表的时候,需要对查询出来的列表数据进行分组统计,首先想到的是避免频繁去操作数据库可以使用 Linq eg: //例如对列表中的Cu元素进行按年GroupBy分组统计 //包含年份,平均 ...