通过.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作为开 ...
随机推荐
- String/StringBuilder 类 用对象数组实现登录注册功能
一.需求说明:实现用户注册.登陆功能: 程序中使用一个长度为3的对象数组,存储用户的登录名和密码: 例如如下格式: 登录名 密码 生日 爱好 zhangsan 11 ...
- 前端学习笔记2017.6.12 DIV布局网页
DIV的功能就是把网页划分成逻辑块的. 看下豆瓣东西页面的布局,我们来分析下. 按照先从上到下的原则,把这个页面分成几个块: 首先是最顶端的这个条,这是一个DIV,我们给它起个名字,叫banner 然 ...
- linux 开启终端256色支持
一.简介 一般的Linux发行版默认的终端都是16色的,但事实上几乎所有的终端都支持256色终端.本文介绍开启终端256色支持的方法. 二.操作步骤 1)检查终端是否支持256色 http://www ...
- Effective Objective-C [下]
http://esoftmobile.com/2013/08/17/effective-objective-c-2/ Chapter 6: Blocks and Grand Central Dispa ...
- STL--C++中 destory() 和deallocate()以及delete函数的相关性和区别性,destorydeallocate
这里非常的绕口 需要仔细的来看看: destory(): 显示调用一个对象的析构函数 相当于释放一个对象需要释放的一些动态内存 为下次真正释放对象做准备 deallocate():真正的释放一个内存 ...
- Appium的常用定位方法
使用uiautomatorviewer.bat工具来找到属性定位元素,在SDK的tools目录下找到该工具,双击打开.左边框内展示app的界面元素,右上角框内展示元素的层级关系,右下角框内展示元素的属 ...
- c++内联 inline
内联声明只是建议 ,不一定就会内联. http://www.voidcn.com/blog/u011327981/article/p-5006835.html
- GoogLeNet InceptionV2/V3/V4
仅用作自己学习 这篇文章中我们会详细讲到Inception V2/V3/V4的发展历程以及它们的网络结构和亮点. GoogLeNet Inception V2 GoogLeNet Inc ...
- SourceTree——MAC OSX下的Git GUI客户端
在MAC下面为Git找一款用得顺手的GUI客户端还真不容易.学习工具使用还是先器而后道的路径比较适合我,当年上手CVS SVN都是如此,先通过tortoise客户端熟练了基本操作,之后在搭建构建平台的 ...
- vue使用过程常见的一些问题
Vue目前的的开发模式主要有两种:1.直接页面级的开发,script直接引入Vue2.工程性开发,webpack+loader或者直接使用脚手架工具Vue-cli,里面的文件都配置好了 webpack ...