在看过一篇大神写的《带你看懂Dictionary的内部实现》,对Dictionary的内部实现有了一个清晰的了解,但纸上得来终觉浅,作为程序员,还是自己调试一下代码,记忆更深刻,为此专门拷贝出C#的Dictionary源码,因为源码有很多保护级别的代码,不能直接运行。下面贴出我调试没有问题的Dictionary源码(有部分代码没有重写),并附带官方调用实例,方便断点调试。

Dictionary源码:

using System;
using System.Collections;
using System.Collections.Generic;namespace StructScript
{/// <summary>/// 哈希表的查找算法主要分为两步:/// 第一步是用哈希函数将键转换为数组的一个索引,理想情况下不同的键都能转换为不同的索引值,但是实际上会有多个键哈希到到相同索引值上。/// 因此,第二步就是处理碰撞冲突的过程。这里有两种处理碰撞冲突的方法:separate chaining(拉链法)和linear probing(线性探测法)。/// </summary>public class DictionaryScript<TKey, TValue> : IDictionary<TKey, TValue>{protected struct Entry{public int hashCode;    //31位散列值,32最高位表示符号位,-1表示未使用public int next;        //下一项的索引值,-1表示结尾public TKey key;        //键public TValue value;    //值}protected int[] buckets;//处理hash碰撞,储存由键转换成的数组索引protected Entry[] entries;//元素数组,用于维护哈希表中的数据protected int count;//元素数量protected int freeList;//空闲的列表protected int freeCount;//空闲列表元素数量protected IEqualityComparer<TKey> comparer;//哈希表中的比较函数protected KeyCollection keys;//键集合protected ValueCollection values;//值集合protected const int MaxPrimeArrayLength = 0x7FEFFFFD;//预设素数数组protected static readonly int[] primes = {3, 7, 11, 17, 23, 29, 37, 47, 59, 71, 89, 107, 131, 163, 197, 239, 293, 353, 431, 521, 631, 761, 919,1103, 1327, 1597, 1931, 2333, 2801, 3371, 4049, 4861, 5839, 7013, 8419, 10103, 12143, 14591,17519, 21023, 25229, 30293, 36353, 43627, 52361, 62851, 75431, 90523, 108631, 130363, 156437,187751, 225307, 270371, 324449, 389357, 467237, 560689, 672827, 807403, 968897, 1162687, 1395263,1674319, 2009191, 2411033, 2893249, 3471899, 4166287, 4999559, 5999471, 7199369};//调用本身的构造方法public DictionaryScript() : this(0, null) { }public DictionaryScript(int capacity) : this(capacity, null) { }public DictionaryScript(int capacity, IEqualityComparer<TKey> comparer){if (capacity < 0){throw new ArgumentNullException();}if (capacity > 0){Initialize(capacity);}this.comparer = comparer == null ? EqualityComparer<TKey>.Default : comparer;}/// <summary>/// 需要一个大小为M的数组来储存键值对,那么需要一个能够将任意键转为该数组范围内的索引(0到M-1)的哈希函数/// 这个哈希函数应该易于计算并且能够均匀分布所有的键。即对于任意键,0到M-1之间的每个整数都有相等可能性与之对应/// 除留余数法。这个方法选择大小为素数M的数组,对于任意正整数k,计算k除以M的余数/// 如果M不是素数的话,将不能有效利用键中所包含的所有信息,导致算法不能均匀地分布所有键。/// </summary>private void Initialize(int capacity){//根据构造函数设定的初始容量,获取一个大于并接近的素数int size = GetPrime(capacity);buckets = new int[size];for (int i = 0; i < buckets.Length; i++){buckets[i] = -1;}entries = new Entry[size];freeList = -1;}/// <summary>/// 根据构造函数设定的初始容量,获取一个大于并接近的素数/// </summary>public int GetPrime(int min){if (min < 0)throw new ArgumentException();for (int i = 0; i < primes.Length; i++){int prime = primes[i];if (prime >= min) return prime;}//如果超出预先的数组for (int i = (min | 1); i < Int32.MaxValue; i += 2){if (IsPrime(i) && ((i - 1) % 101 != 0))return i;}return min;}/// <summary>/// 是否是素数/// </summary>private bool IsPrime(int candidate){if ((candidate & 1) != 0){int limit = (int)Math.Sqrt(candidate);for (int divisor = 3; divisor <= limit; divisor += 2){if ((candidate % divisor) == 0)return false;}return true;}return (candidate == 2);}/// <summary>/// 扩容/// </summary>private int ExpandPrime(int oldSize){int newSize = 2 * oldSize;if ((uint)newSize > MaxPrimeArrayLength && MaxPrimeArrayLength > oldSize){return MaxPrimeArrayLength;}return GetPrime(newSize);}public TValue this[TKey key]{get{int i = FindEntry(key);if (i >= 0){return entries[i].value;}else{throw new KeyNotFoundException();}}set{Insert(key, value, false);}}public int Count{get{return count;}}public KeyCollection Keys{get{if (keys == null) keys = new KeyCollection(this);return keys;}}public ValueCollection Values{get{if (values == null) values = new ValueCollection(this);return values;}}IEnumerable<TKey> IDictionary<TKey, TValue>.Keys{get{if (keys == null) keys = new KeyCollection(this);return keys;}}IEnumerable<TValue> IDictionary<TKey, TValue>.Values{get{if (values == null) values = new ValueCollection(this);return values;}}public void Add(KeyValuePair<TKey, TValue> item){Add(item.Key, item.Value);}public void Add(TKey key, TValue value){Insert(key, value, true);}private void Insert(TKey key, TValue value, bool add){//key不能为空,value可以为空if (key == null){throw new ArgumentNullException();}if (buckets == null){Initialize(0);}int collisionCount = 0;int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;//将HashCode的返回值转化为数组索引int bucketIndex = hashCode % buckets.Length;// 处理hash碰撞冲突// 如果转换出的bucketIndex大于等于0,判断buckets数组中有没有相等的,如果相等,需要处理冲突for (int i = buckets[bucketIndex]; i >= 0; i = entries[i].next){//如果转换的hash值与之前已经添加的hash值相等,同时插入的key与之前的相同,处理冲突,key是唯一的,不能重复if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)){if (add){throw new ArgumentException();}entries[i].value = value;return;}collisionCount++;}//数组索引int index;//如果空链表的长度大于0,FreeList链表不为空,因此字典会优先把新增元素添加到FreeList链表所指向的位置,添加后FreeCount减1if (freeCount > 0){index = freeList;freeList = entries[index].next;freeCount--;}else{//如果数组已满,需扩容if (count == entries.Length){Resize();bucketIndex = hashCode % buckets.Length;}index = count;count++;}entries[index].hashCode = hashCode;//新增元素的next指向上一个元素的索引entries[index].next = buckets[bucketIndex];entries[index].key = key;entries[index].value = value;//记录新增元素的索引buckets[bucketIndex] = index;// 冲突数达到了一定的阈值,之后更新Hash值if (collisionCount > 100 && IsEqualityComparer(comparer)){comparer = EqualityComparer<TKey>.Default;Resize(entries.Length, true);}}private bool IsEqualityComparer(object comparer){return (comparer == null || comparer == EqualityComparer<string>.Default);}/// <summary>/// 扩容数组/// </summary>private void Resize(){Resize(ExpandPrime(count), false);}private void Resize(int newSize, bool forceNewHashCodes){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);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;}public void Clear(){if (count > 0){for (int i = 0; i < buckets.Length; i++) buckets[i] = -1;Array.Clear(entries, 0, count);freeList = -1;count = 0;freeCount = 0;}}public bool Contains(KeyValuePair<TKey, TValue> item){return ContainsKey(item.Key);}public bool ContainsKey(TKey key){return FindEntry(key) >= 0;}public bool ContainsValue(TValue value){if (value == null){for (int i = 0; i < count; i++){if (entries[i].hashCode >= 0 && entries[i].value == null) return true;}}else{EqualityComparer<TValue> c = EqualityComparer<TValue>.Default;for (int i = 0; i < count; i++){if (entries[i].hashCode >= 0 && c.Equals(entries[i].value, value)) return true;}}return false;}public void CopyTo(KeyValuePair<TKey, TValue>[] array, int arrayIndex){throw new NotImplementedException();}public bool Remove(KeyValuePair<TKey, TValue> item){return Remove(item.Key);}public bool Remove(TKey key){if (key == null){throw new ArgumentNullException();}if (buckets != null){int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;int bucket = hashCode % buckets.Length;int last = -1;for (int i = buckets[bucket]; i >= 0; last = i, i = entries[i].next){if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)){//如果key在索引0,直接找到或者遍历到数组索引0找到keyif (last < 0){// 把当前索引的bucket数组中的值重置,设置为-1buckets[bucket] = entries[i].next;}else{//遍历数组时,找到key,把当前删除元素的下一个元素的索引赋值给当前删除元素的上一个元素的nextentries[last].next = entries[i].next;}entries[i].hashCode = -1;entries[i].next = freeList;entries[i].key = default(TKey);entries[i].value = default(TValue);freeList = i;freeCount++;return true;}}}return false;}public bool TryGetValue(TKey key, out TValue value){int i = FindEntry(key);if (i >= 0){value = entries[i].value;return true;}value = default(TValue);return false;}private int FindEntry(TKey key){if (key == null){throw new ArgumentNullException();}if (buckets != null){int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;for (int i = buckets[hashCode % buckets.Length]; i >= 0; i = entries[i].next){if (entries[i].hashCode == hashCode && comparer.Equals(entries[i].key, key)) return i;}}return -1;}public Enumerator GetEnumerator(){return new Enumerator(this, Enumerator.KeyValuePair);}IEnumerator<KeyValuePair<TKey, TValue>> IEnumerable<KeyValuePair<TKey, TValue>>.GetEnumerator(){return new Enumerator(this, Enumerator.KeyValuePair);}IEnumerator IEnumerable.GetEnumerator(){return new Enumerator(this, Enumerator.DictEntry);}public struct Enumerator : IEnumerator<KeyValuePair<TKey, TValue>>{private DictionaryScript<TKey, TValue> dictionary;private int index;private KeyValuePair<TKey, TValue> current;private int getEnumeratorRetType;internal const int DictEntry = 1;internal const int KeyValuePair = 2;internal Enumerator(DictionaryScript<TKey, TValue> dictionary, int getEnumeratorRetType){this.dictionary = dictionary;index = 0;this.getEnumeratorRetType = getEnumeratorRetType;current = new KeyValuePair<TKey, TValue>();}public bool MoveNext(){while ((uint)index < (uint)dictionary.count){if (dictionary.entries[index].hashCode >= 0){current = new KeyValuePair<TKey, TValue>(dictionary.entries[index].key, dictionary.entries[index].value);index++;return true;}index++;}index = dictionary.count + 1;current = new KeyValuePair<TKey, TValue>();return false;}public KeyValuePair<TKey, TValue> Current{get { return current; }}public void Dispose(){}object IEnumerator.Current{get{if (getEnumeratorRetType == DictEntry){return new DictionaryEntry(current.Key, current.Value);}else{return new KeyValuePair<TKey, TValue>(current.Key, current.Value);}}}void IEnumerator.Reset(){index = 0;current = new KeyValuePair<TKey, TValue>();}}public sealed class KeyCollection : ICollection<TKey>{private DictionaryScript<TKey, TValue> dictionary;public KeyCollection(DictionaryScript<TKey, TValue> dictionary){if (dictionary == null){throw new ArgumentNullException();}this.dictionary = dictionary;}public KeyEnumerator GetEnumerator(){return new KeyEnumerator(dictionary);}IEnumerator<TKey> IEnumerable<TKey>.GetEnumerator(){return new KeyEnumerator(dictionary);}IEnumerator IEnumerable.GetEnumerator(){return new KeyEnumerator(dictionary);}public int Count{get { return dictionary.Count; }}public void Add(TKey item){throw new NotSupportedException();}public void Clear(){throw new NotSupportedException();}public bool Contains(TKey item){return dictionary.ContainsKey(item);}public void CopyTo(TKey[] array, int arrayIndex){throw new NotSupportedException();}public bool Remove(TKey item){throw new NotSupportedException();}}public struct KeyEnumerator : IEnumerator<TKey>, IEnumerator{private DictionaryScript<TKey, TValue> dictionary;private int index;private TKey currentKey;internal KeyEnumerator(DictionaryScript<TKey, TValue> dictionary){this.dictionary = dictionary;index = 0;currentKey = default(TKey);}public void Dispose(){}public bool MoveNext(){while ((uint)index < (uint)dictionary.count){if (dictionary.entries[index].hashCode >= 0){currentKey = dictionary.entries[index].key;index++;return true;}index++;}index = dictionary.count + 1;currentKey = default(TKey);return false;}public TKey Current{get{return currentKey;}}Object IEnumerator.Current{get{if (index <= 0 || (index > dictionary.count)){throw new IndexOutOfRangeException();}return currentKey;}}void IEnumerator.Reset(){index = 0;currentKey = default(TKey);}}public sealed class ValueCollection : ICollection<TValue>{private DictionaryScript<TKey, TValue> dictionary;public ValueCollection(DictionaryScript<TKey, TValue> dictionary){if (dictionary == null){throw new ArgumentNullException();}this.dictionary = dictionary;}public ValueEnumerator GetEnumerator(){return new ValueEnumerator(dictionary);}IEnumerator<TValue> IEnumerable<TValue>.GetEnumerator(){return new ValueEnumerator(dictionary);}IEnumerator IEnumerable.GetEnumerator(){return new ValueEnumerator(dictionary);}public int Count{get { return dictionary.Count; }}public void CopyTo(TValue[] array, int arrayIndex){throw new NotSupportedException();}public void Add(TValue item){throw new NotSupportedException();}public void Clear(){throw new NotSupportedException();}public bool Contains(TValue item){return dictionary.ContainsValue(item);}public bool Remove(TValue item){throw new NotSupportedException();}}public struct ValueEnumerator : IEnumerator<TValue>, IEnumerator{private DictionaryScript<TKey, TValue> dictionary;private int index;private TValue currentValue;internal ValueEnumerator(DictionaryScript<TKey, TValue> dictionary){this.dictionary = dictionary;index = 0;currentValue = default(TValue);}public void Dispose(){}public bool MoveNext(){while ((uint)index < (uint)dictionary.count){if (dictionary.entries[index].hashCode >= 0){currentValue = dictionary.entries[index].value;index++;return true;}index++;}index = dictionary.count + 1;currentValue = default(TValue);return false;}public TValue Current{get{return currentValue;}}Object IEnumerator.Current{get{if (index <= 0 || (index > dictionary.count)){throw new IndexOutOfRangeException();}return currentValue;}}void IEnumerator.Reset(){index = 0;currentValue = default(TValue);}}}
}

Dictionary官方调用实例:

using System;
using System.Collections.Generic;
using System.Collections;namespace StructScript
{public class TestDictionary{static void Main(string[] args){DictionaryScript<int, string> testDic = new DictionaryScript<int, string>(6);testDic.Add(4, "4");//在容量为6的情况下,传入4和11的key,得到的hashcode都是一样的,这里就要处理hash碰撞的问题testDic.Add(11, "11");DictionaryScript<int, string> test1Dic = new DictionaryScript<int, string>(6);test1Dic.Add(4, "4");test1Dic.Add(10, "11");test1Dic.Add(9, "11");test1Dic.Add(8, "11");test1Dic.Add(7, "11");test1Dic.Add(6, "11");test1Dic.Add(5, "11");//根据构造函数设定的初始容量,获取一个大于并接近的素数7//C#内部有一个素数数组,在DictionaryScript的初始化函数Initialize中有具体实现//超出数组容量,需要扩容,这里有数组扩容操作test1Dic.Add(3, "11");string value1;test1Dic.TryGetValue(2, out value1);test1Dic.Remove(3);//下面是官方调用实例DictionaryScript<string, string> openWith = new DictionaryScript<string, string>();openWith.Add("txt", "notepad.exe");openWith.Add("bmp", "paint.exe");openWith.Add("dib", "paint.exe");openWith.Add("rtf", "wordpad.exe");// The Add method throws an exception if the new key is // already in the dictionary.try{openWith.Add("txt", "winword.exe");}catch (ArgumentException){Console.WriteLine("An element with Key = \"txt\" already exists.");}// The Item property is another name for the indexer, so you // can omit its name when accessing elements. Console.WriteLine("For key = \"rtf\", value = {0}.", openWith["rtf"]);// The indexer can be used to change the value associated// with a key.openWith["rtf"] = "winword.exe";Console.WriteLine("For key = \"rtf\", value = {0}.",openWith["rtf"]);// If a key does not exist, setting the indexer for that key// adds a new key/value pair.openWith["doc"] = "winword.exe";// The indexer throws an exception if the requested key is// not in the dictionary.try{Console.WriteLine("For key = \"tif\", value = {0}.",openWith["tif"]);}catch (KeyNotFoundException){Console.WriteLine("Key = \"tif\" is not found.");}// When a program often has to try keys that turn out not to// be in the dictionary, TryGetValue can be a more efficient // way to retrieve values.string value = "";if (openWith.TryGetValue("tif", out value)){Console.WriteLine("For key = \"tif\", value = {0}.", value);}else{Console.WriteLine("Key = \"tif\" is not found.");}// ContainsKey can be used to test keys before inserting // them.if (!openWith.ContainsKey("ht")){openWith.Add("ht", "hypertrm.exe");Console.WriteLine("Value added for key = \"ht\": {0}",openWith["ht"]);}// When you use foreach to enumerate dictionary elements,// the elements are retrieved as KeyValuePair objects.Console.WriteLine();foreach (KeyValuePair<string, string> kvp in openWith){Console.WriteLine("Key = {0}, Value = {1}",kvp.Key, kvp.Value);}// To get the values alone, use the Values property.DictionaryScript<string, string>.ValueCollection valueColl =openWith.Values;// The elements of the ValueCollection are strongly typed// with the type that was specified for dictionary values.Console.WriteLine();foreach (string s in valueColl){Console.WriteLine("Value = {0}", s);}// To get the keys alone, use the Keys property.DictionaryScript<string, string>.KeyCollection keyColl =openWith.Keys;// The elements of the KeyCollection are strongly typed// with the type that was specified for dictionary keys.Console.WriteLine();foreach (string s in keyColl){Console.WriteLine("Key = {0}", s);}// Use the Remove method to remove a key/value pair.Console.WriteLine("\nRemove(\"doc\")");openWith.Remove("doc");if (!openWith.ContainsKey("doc")){Console.WriteLine("Key \"doc\" is not found.");}Console.ReadLine();}}
}

C#数据结构-Dictionary实现相关推荐

  1. 肝!Python 教程:从零到大师

    翻译:Summer   链接: https://pythoncaff.com/topics/104/python-tutorials-from-zero-to-master-suitable-for- ...

  2. python三年a班的成绩_Python学习小结

    2016年12月18日安装 了Python 2.7.13,然后跟着贺鹏飞老师的课程从零开始学习python编程.这周终于写完所有题目的代码并跑出正确的结果,这里写一个小结整理一下这段时间的学习. 贺老 ...

  3. 使用打字稿绘图网格构建游戏3 5

    Chapter III in the series of tutorials on how to build a game from scratch with TypeScript and nativ ...

  4. vim 高级应用 原文地址 http://www.2maomao.com/blog/wp-content/uploads/vim_tips.txt

    最佳vim技巧 ---------------------------------------- # 信息来源 ---------------------------------------- www ...

  5. 倒排索引的数据结构:Term index、Term Dictionary、Posting List

    2.倒排索引的数据结构 倒排索引其实包含了三种数据,分别是 倒排表(Posting List) 词项字典(Term Dictionary) 词项索引(Term Index) 这几种文件分别存储了不同的 ...

  6. JavaScript数据结构——字典(Dictionary)

    概念和结构 字典里面的元素都是一个键(key)值(value)对. 字典里面的元素的键(key)不能重复,值(value)可以重复. 字典的操作 字典有八种常用操作,分别为 检查键是否存在 has(k ...

  7. 数据结构与算法笔记:贪心策略之BSTBBST, Hashtable+Dictionary+Map, Priority Queue~Heap, Minium Spanning Tree

    BST & BBST BST(Binary Search Tree) 二叉搜索树,也就是使用二叉树来做查找 BBST(Balanced Binary Search Tree) 平衡二叉搜索树 ...

  8. C#中使用Dictionary实现Map数据结构——VC编程网

    转载自: http://blog.51cto.com/psnx168 在VC中使用过CMap以及在Java中使用过Map的朋友应该很熟悉,使用Map可以方便实现基于键值对数据的处理,在C#中,你就需要 ...

  9. Python3-Cookbook总结 - 第一章:数据结构和算法

    第一章:数据结构和算法 Python 提供了大量的内置数据结构,包括列表,集合以及字典.大多数情况下使用这些数据结构是很简单的. 但是,我们也会经常碰到到诸如查询,排序和过滤等等这些普遍存在的问题. ...

最新文章

  1. cocos2d-x在win7下的android交叉编译环境
  2. 基于SD卡的FatFs文件系统(FatFs移植到STM32)
  3. sklearn中的Pipline(流水线学习器)
  4. MyBatis 缓存详解-cache 缓存
  5. php nginx配置404页面,Nginx实现404页面的几种方法
  6. 前端知识点总结---面试专用
  7. 《四世同堂》金句摘抄(五)
  8. C++ STL 迭代器5种类型 简介
  9. mysql导出选择两张表,Mysql导出(多张表)表结构及表数据 mysqldump用法
  10. pip指定源安装_几种python安装简单方法
  11. 浅谈构造函数(c#)
  12. Vivado入门创建工程之----自定义ip及其使用
  13. CSS中文手册下载、使用技巧(附下载链接,压缩包被禁用了)
  14. 2014全新增强版迅捷PDF转换器介绍
  15. 软考数据库系统工程师教程知识点总结
  16. 叫号系统是否需要服务器,银行排队叫号方法及系统、服务器及存储介质
  17. 极品抓鸡36课------8080端口入侵笔记
  18. linux下解压bin文件怎么打开方式,安卓手机如何打开.bin文件?
  19. MICRO SIM卡 SIM小卡 尺寸图及剪卡图解
  20. Screenpresso-截图工具绿色版

热门文章

  1. kafka启用Kerberos认证
  2. Veritas NetBackup还原文件数据
  3. matlab中b逆,matlab中,表示()A.矩阵A的逆右乘BB.B矩阵A的逆左乘BC.矩阵B的逆左乘AD.矩阵B的逆右乘A...
  4. QQ怎么 发送 已经录好的视频
  5. 触动精灵:零基础小白学触动15-16
  6. [BZOJ3231] [Sdoi2008]递归数列
  7. 赋能信创生态,巨杉数据库获得鲲鹏Validated认证
  8. LWIP UDP 编程
  9. PAT解题思路传送门(柳神)
  10. 多元正态分布的条件分布与边缘分布