所在框架版本:.Net Framework 4.0.30319
桶数组中的值是对应Entry数组的下标,Entry的Next是下一个Entry元素的下标
1.Entry结构体,键值对存放的位置
private struct Entry
{
public int hashCode; // 除符号位以外的31位hashCode值, 如果该Entry没有被使用,那么为-1
public int next; //下一个Entry元素的下标索引,如果没有下一个就为-1
public TKey key; //存放元素的键
public TValue value; // 存放元素的值
}
2.部分变量
private int[] buckets; // Hash桶
private Dictionary<TKey, TValue>.Entry[] entries; // Entry数组,存放元素
private int count; // 已有的键值对个数
private int version; // 当前版本,防止迭代过程中集合被更改
private int freeList; // 被删除Entry在entries中的下标,代表这个位置是空闲的
private int freeCount; // 有多少个被删除的Entry,有多少个空闲的位置
private IEqualityComparer<TKey> comparer; // 比较器
private Dictionary<TKey, TValue>.KeyCollection keys; // Key的集合
private Dictionary<TKey, TValue>.ValueCollection values; // Value的集合
3.初始化
private void Initialize(int capacity) //参数是初始容量
{
int prime = HashHelpers.GetPrime(capacity); //得到大于容量的最小质数作为最终容量
this.buckets = new int[prime]; //初始化桶数组
for (int index = 0; index < this.buckets.Length; ++index)
this.buckets[index] = -1; //每个桶默认值为-1
this.entries = new Dictionary<TKey, TValue>.Entry[prime]; //初始化Entry数组
this.freeList = -1; //freeList<0,代表Entry数组中没有空闲
}
4.插入
private void Insert(TKey key, TValue value, bool add) //键,值,是否是添加操作
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets == null) //如果桶数组为空,重新初始化
this.Initialize(0);
int num1 = this.comparer.GetHashCode(key) & int.MaxValue; //通过key获取hash值,&这样的操作通常用于确保哈希码为正数,即将哈希码限制在非负整数范围内
int index1 = num1 % this.buckets.Length; //hash值%桶数组的长度得到在哪个桶中,即桶数组的下标
int num2 = 0; //hash碰撞次数
for (int index2 = this.buckets[index1]; index2 >= 0; index2 = this.entries[index2].next) //遍历判断传入的键是否已经存在
{
if (this.entries[index2].hashCode == num1 && this.comparer.Equals(this.entries[index2].key, key))
{
if (add) //如果是Add操作,抛出异常
ThrowHelper.ThrowArgumentException(ExceptionResource.Argument_AddingDuplicate);
this.entries[index2].value = value; //如果不是Add操作,比如dic[1] = 2这种情况,直接赋新值
++this.version; //版本号更新
return;
}
++num2;
}
int index3; //Entry数组的下标
if (this.freeCount > 0) //freeCount>0,说明之前有删除元素
{
index3 = this.freeList; //将被删除元素的Entry下标给到新元素
this.freeList = this.entries[index3].next; //freeList 更新为当前元素的next Entry数组下标
--this.freeCount; //空闲个数减一
}
else
{
if (this.count == this.entries.Length) //如果Entry数组已满
{
this.Resize(); //扩容
index1 = num1 % this.buckets.Length; //重新获得对应桶的下标
}
index3 = this.count; //默认取Entry数组中未使用的第一个
++this.count;
}
this.entries[index3].hashCode = num1; // 给当前Entry赋值
this.entries[index3].next = this.buckets[index1];
this.entries[index3].key = key;
this.entries[index3].value = value;
this.buckets[index1] = index3; //桶数组的值变成当前Entry元素的下标
++this.version; //版本号更新
// 如果碰撞次数大于设置的最大碰撞次数,那么触发Hash碰撞扩容
if (num2 <= 100 || !HashHelpers.IsWellKnownEqualityComparer((object) this.comparer))
return;
this.comparer = (IEqualityComparer<TKey>) HashHelpers.GetRandomizedEqualityComparer((object) this.comparer);
this.Resize(this.entries.Length, true);
}
5.查找
private int FindEntry(TKey key)
{
if ((object) key == null) //如果查找键为空,抛出异常
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets != null)
{
int num = this.comparer.GetHashCode(key) & int.MaxValue; //得到键对应的hash值
for (int index = this.buckets[num % this.buckets.Length]; index >= 0; index = this.entries[index].next)
{ //遍历entries链
if (this.entries[index].hashCode == num && this.comparer.Equals(this.entries[index].key, key)) //如果查找到了对应的键,直接返回对应的Entry元素下标
return index;
}
}
return -1;
}
public bool TryGetValue(TKey key, out TValue value)
{
int entry = this.FindEntry(key);
if (entry >= 0)
{
value = this.entries[entry].value; //通过FindEntry查找到的下标,找到对应的值
return true;
}
value = default (TValue);
return false;
}
public bool ContainsKey(TKey key) => this.FindEntry(key) >= 0; //直接看FindEntry是否找到下标
public bool ContainsValue(TValue value)
{
if ((object) value == null) //如果要查找的值为空
{
for (int index = 0; index < this.count; ++index) //遍历entries数组
{
if (this.entries[index].hashCode >= 0 && (object) this.entries[index].value == null) //如果hash值大于0且值为空,说明找到了
return true;
}
}
else
{
EqualityComparer<TValue> equalityComparer = EqualityComparer<TValue>.Default;
for (int index = 0; index < this.count; ++index) //依旧遍历entries数组
{
if (this.entries[index].hashCode >= 0 && equalityComparer.Equals(this.entries[index].value, value)) //看hash值的同时也看值是否相等
return true;
}
}
return false;
}
6.移除
public bool Remove(TKey key)
{
if ((object) key == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.key);
if (this.buckets != null)
{
int num = this.comparer.GetHashCode(key) & int.MaxValue; //得到hash值
int index1 = num % this.buckets.Length; //获得对应的桶
int index2 = -1; // 用于确定是否当前entries链的第一个元素
for (int index3 = this.buckets[index1]; index3 >= 0; index3 = this.entries[index3].next) //遍历entries链
{
if (this.entries[index3].hashCode == num && this.comparer.Equals(this.entries[index3].key, key)) //如果找到了对应的键
{
if (index2 < 0) //如果要移除的是第一个元素,则对应桶存储当前元素的Next元素下标
this.buckets[index1] = this.entries[index3].next;
else //不是第一个,需要将上一个元素的Next指向当前元素的Next,相当于越过了当前元素
this.entries[index2].next = this.entries[index3].next;
//将Entry内的数据初始化
this.entries[index3].hashCode = -1;
this.entries[index3].next = this.freeList;
this.entries[index3].key = default (TKey);
this.entries[index3].value = default (TValue);
this.freeList = index3; //freeList等于当前的Entry元素的位置,下一次添加元素会优先添加到该位置
++this.freeCount; //空闲个数加一
++this.version; //版本加一
return true;
}
index2 = index3; //不断在这个链表上往后移动
}
}
return false;
}
7.扩容
private void Resize() => this.Resize(HashHelpers.ExpandPrime(this.count), false);
private void Resize(int newSize, bool forceNewHashCodes)
{
int[] numArray = new int[newSize]; //申请新的桶数组
for (int index = 0; index < numArray.Length; ++index)
numArray[index] = -1;
Dictionary<TKey, TValue>.Entry[] entryArray = new Dictionary<TKey, TValue>.Entry[newSize]; //申请新的Entry数组
Array.Copy((Array) this.entries, 0, (Array) entryArray, 0, this.count); //将原来的Entry数组的内容拷贝到新的Entry数组
if (forceNewHashCodes) //如果是hash碰撞扩容,需要重新计算hash值(指只用了一个桶,但是Entry数组被使用完的情况)
{
for (int index = 0; index < this.count; ++index)
{
if (entryArray[index].hashCode != -1)
entryArray[index].hashCode = this.comparer.GetHashCode(entryArray[index].key) & int.MaxValue;
}
}
for (int index1 = 0; index1 < this.count; ++index1) //遍历原来的Entry数组,重新建立Entry链表
{
if (entryArray[index1].hashCode >= 0)
{
int index2 = entryArray[index1].hashCode % newSize; //获得新的桶数组下标
entryArray[index1].next = numArray[index2]; //重新建立entries 链表
numArray[index2] = index1;
}
}
//将数组赋值会原来
this.buckets = numArray;
this.entries = entryArray;
}
8.版本控制
字典类内部实现了迭代器相关的方法
public struct Enumerator :
IEnumerator<KeyValuePair<TKey, TValue>>,
IDisposable,
IEnumerator,
IDictionaryEnumerator
{
private Dictionary<TKey, TValue> dictionary;
private int version;
private int index;
private KeyValuePair<TKey, TValue> current;
private int getEnumeratorRetType;
internal const int DictEntry = 1;
internal const int KeyValuePair = 2;
internal Enumerator(Dictionary<TKey, TValue> dictionary, int getEnumeratorRetType) //构造函数
{
this.dictionary = dictionary;
this.version = dictionary.version; //版本
this.index = 0;
this.getEnumeratorRetType = getEnumeratorRetType;
this.current = new KeyValuePair<TKey, TValue>();
}
[__DynamicallyInvokable]
public bool MoveNext() //MoveNext方法
{
if (this.version != this.dictionary.version)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
for (; (uint) this.index < (uint) this.dictionary.count; ++this.index)
{
if (this.dictionary.entries[this.index].hashCode >= 0)
{
this.current = new KeyValuePair<TKey, TValue>(this.dictionary.entries[this.index].key, this.dictionary.entries[this.index].value);
++this.index;
return true;
}
}
this.index = this.dictionary.count + 1;
this.current = new KeyValuePair<TKey, TValue>();
return false;
}
[__DynamicallyInvokable]
public KeyValuePair<TKey, TValue> Current //返回current
{
[__DynamicallyInvokable] get => this.current;
}
[__DynamicallyInvokable]
public void Dispose()
{
}
[__DynamicallyInvokable]
object IEnumerator.Current
{
[__DynamicallyInvokable] get
{
if (this.index == 0 || this.index == this.dictionary.count + 1)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
return this.getEnumeratorRetType == 1 ? (object) new DictionaryEntry((object) this.current.Key, (object) this.current.Value) : (object) new KeyValuePair<TKey, TValue>(this.current.Key, this.current.Value);
}
}
[__DynamicallyInvokable]
void IEnumerator.Reset() //Reset方法
{
if (this.version != this.dictionary.version)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumFailedVersion);
this.index = 0;
this.current = new KeyValuePair<TKey, TValue>();
}
[__DynamicallyInvokable]
DictionaryEntry IDictionaryEnumerator.Entry
{
[__DynamicallyInvokable] get
{
if (this.index == 0 || this.index == this.dictionary.count + 1)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
return new DictionaryEntry((object) this.current.Key, (object) this.current.Value);
}
}
[__DynamicallyInvokable]
object IDictionaryEnumerator.Key
{
[__DynamicallyInvokable] get
{
if (this.index == 0 || this.index == this.dictionary.count + 1)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
return (object) this.current.Key;
}
}
[__DynamicallyInvokable]
object IDictionaryEnumerator.Value
{
[__DynamicallyInvokable] get
{
if (this.index == 0 || this.index == this.dictionary.count + 1)
ThrowHelper.ThrowInvalidOperationException(ExceptionResource.InvalidOperation_EnumOpCantHappen);
return (object) this.current.Value;
}
}
}
9.清除
public void Clear()
{
if (this.count <= 0)
return;
for (int index = 0; index < this.buckets.Length; ++index)
this.buckets[index] = -1; //重置桶数组
Array.Clear((Array) this.entries, 0, this.count); //重置Entry数组
this.freeList = -1; //重置freeList
this.count = 0;
this.freeCount = 0;
++this.version; //版本号加一
}
10.键值对数量
public int Count
{
[__DynamicallyInvokable] get => this.dictionary.Count; //直接返回count
}
11.复制
public void CopyTo(TKey[] array, int index)
{
if (array == null)
ThrowHelper.ThrowArgumentNullException(ExceptionArgument.array);
if (index < 0 || index > array.Length)
ThrowHelper.ThrowArgumentOutOfRangeException(ExceptionArgument.index, ExceptionResource.ArgumentOutOfRange_NeedNonNegNum);
if (array.Length - index < this.dictionary.Count) //要存放的空间太小,放不下
ThrowHelper.ThrowArgumentException(ExceptionResource.Arg_ArrayPlusOffTooSmall);
int count = this.dictionary.count;
Dictionary<TKey, TValue>.Entry[] entries = this.dictionary.entries;
for (int index1 = 0; index1 < count; ++index1)
{
if (entries[index1].hashCode >= 0) //如果entries有值
array[index++] = entries[index1].key; //将entries数组的键逐个拷贝到数组中
}
}