前言:很多文章描述過於復雜故整理之。
1C# Dictionary設計思想:
1.1 數據結構
- 創建大小為size的數組entries(用來存放字典元素--以下稱:entry)
- 創建桶buckets數組記錄entry的index(大小和entries保持一致)
- entry結構體:hash、nextIndex(下個entry的index)、key、value
1.2 數據維護(邏輯)
- 添加entry時,計算該entry hashCode落在桶的位置(規則是hashCode對桶求余)
- 當出現落在的位置(桶)里已經有別的“index”了,那么這些沖突的index根據先來后到建立單鏈表(即鏈地址法--開放地址法、鏈地址法、建立溢出區、再哈希法4大方法之一) 可以發現,最理想的狀態是,每個entry一一對應的落在buckets數組上是最划算的(查詢等消耗最低)。
- 當entries數組長度不夠用時擴容,buckets也需要同步擴容。
- 記錄freeCount,freeCount == count(entries的)-Count(字典的)
- 字典有個版本字段,增刪改都會導致+1。(意義:比如遍歷的過程是否版本被修改--增刪)(freeList;//記錄最近一次remove的實體的idx)
1.3 鏈地址圖示--桶沖突
1.4 部分源碼例子
private void Resize(int newSize, bool forceNewHashCodes) {
Contract.Assert(newSize >= entries.Length);
//創建新的桶數組
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);
//強制重新計算hashCode
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) {
//當hashCode大於-1(該元素有值),賦值桶的值
int bucket = newEntries[i].hashCode % newSize;
newEntries[i].next = newBuckets[bucket];
newBuckets[bucket] = i;
}
}
buckets = newBuckets;
entries = newEntries;
}
Find操作
private int FindEntry(TKey key) {
...
int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
//對應的桶拿到該桶位置最近一次的實體的idx, 遍歷桶位置的單鏈表,如果對應的key和參數key相等,則return
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;
}
建議查看源碼深入理解