C#中Dictionary的內部實現剖析
瞭解Dictionary的開發人員都瞭解,和List相比,字典新增會慢,但是查詢會比較快,那麼Dictionary是如何實現的呢?
Dictionary的構造
下面的程式碼我看看Dictionary在構造時都做了什麼:
private void Initialize(int capacity) { int prime = HashHelpers.GetPrime(capacity); this.buckets = new int[prime]; for (int i = 0; i < this.buckets.Length; i++) { this.buckets[i] = -1; } this.entries = new Entry<TKey, TValue>[prime]; this.freeList = -1; }
我們看到,Dictionary在構造的時候做了以下幾件事:
- 初始化一個this.buckets = new int[prime]
- 初始化一個this.entries = new Entry<TKey, TValue>[prime]
- Bucket和entries的容量都為大於字典容量的一個最小的質數
其中this.buckets主要用來進行Hash碰撞,this.entries用來儲存字典的內容,並且標識下一個元素的位置。
我們以Dictionary<int,string> 為例,來展示一下Dictionary如何新增元素:
首先,我們構造一個:
Dictionary<int, string> test = new Dictionary<int, string>(6);
初始化後:
(檢視大圖)
新增元素時,集合內部Bucket和entries的變化
Test.Add(4,”4″)後:
根據Hash演算法: 4.GetHashCode()%7= 4,因此碰撞到buckets中下標為4的槽上,此時由於Count為0,因此元素放在Entries中第0個元素上,新增後Count變為1
(檢視大圖)
Test.Add(11,”11″)
根據Hash演算法 11.GetHashCode()%7=4,因此再次碰撞到Buckets中下標為4的槽上,由於此槽上的值已經不為-1,此時Count=1,因此把這個新加的元素放到entries中下標為1的陣列中,並且讓Buckets槽指向下標為1的entries中,下標為1的entry之下下標為0的entries。
(檢視大圖)
Test.Add(18,”18″)
我們新增18,讓HashCode再次碰撞到Buckets中下標為4的槽上,這個時候新元素新增到count+1的位置,並且Bucket槽指向新元素,新元素的Next指向Entries中下標為1的元素。此時你會發現所有hashcode相同的元素都形成了一個連結串列,如果元素碰撞次數越多,連結串列越長。所花費的時間也相對較多。
(檢視大圖)
Test.Add(19,”19″)
再次新增元素19,此時Hash碰撞到另外一個槽上,但是元素仍然新增到count+1的位置。
(檢視大圖)
刪除元素時集合內部的變化
Test.Remove(4)
我們刪除元素時,通過一次碰撞,並且沿著連結串列尋找3次,找到key為4的元素所在的位置,刪除當前元素。並且把FreeList的位置指向當前刪除元素的位置,FreeCount置為1
(檢視大圖)
Test.Remove(18)
刪除Key為18的元素,仍然通過一次碰撞,並且沿著連結串列尋找2次,找到當前元素,刪除當前元素,並且讓FreeList指向當前元素,當前元素的Next指向上一個FreeList元素。
此時你會發現FreeList指向了一個連結串列,連結串列裡面不包含任何元素,FreeCount表示不包含元素的連結串列的長度。
(檢視大圖)
Test.Add(20,”20″)
再新增一個元素,此時由於FreeList連結串列不為空,因此字典會優先新增到FreeList連結串列所指向的位置,新增後FreeCount減1,FreeList連結串列長度變為1
(檢視大圖)
總結:
通過以上試驗,我們可以發現Dictionary在新增,刪除元素按照如下方法進行:
- 通過Hash演算法來碰撞到指定的Bucket上,碰撞到同一個Bucket槽上所有資料形成一個單連結串列
- 預設情況Entries槽中的資料按照新增順序排列
- 刪除的資料會形成一個FreeList的連結串列,新增資料的時候,優先向FreeList連結串列中新增資料,FreeList為空則按照count依次排列
- 字典查詢及其的效率取決於碰撞的次數,這也解釋了為什麼Dictionary的查詢會很快。
好吧,熬了半宿,今天先寫到這了,如果看了有所收穫就幫忙頂一下,有問題歡迎拍磚。
相關文章
- 帶你看懂Dictionary的內部實現
- KVO的內部實現
- WPF原始碼分析系列一:剖析WPF模板機制的內部實現(一)原始碼
- WPF原始碼分析系列一:剖析WPF模板機制的內部實現(五)原始碼
- mysqldump的內部實現原理MySql
- gostring的內部實現Go
- 字典(Dictionary)的javascript實現JavaScript
- HashMap的內部實現機制HashMap
- 面試官:Redis中列表的內部實現方式是什麼?面試Redis
- iOS 揭露Block的內部實現原理iOSBloC
- 深入 Python 字典的內部實現Python
- 深入 Python 列表的內部實現Python
- SDWebImage內部實現過程Web
- C# 6.0的Dictionary語法C#
- 【C#】-Dictionary的詳細用法C#
- C#:Dictionary轉DataTableC#
- Container及其內部程式監控剖析AI
- Redis字串型別內部編碼剖析Redis字串型別
- 精讀《JS 陣列的內部實現》JS陣列
- kafka的內部實現、安裝和使用Kafka
- Object.create(..)和new(..)的內部實現Object
- ORACLE NUMBER型別內部實現Oracle型別
- 解讀Promise內部實現原理Promise
- DotNet Dictionary 實現簡介
- C# 之 Hashtable 與 DictionaryC#
- iOS中atomic和nonatomic區別及內部實現iOS
- Flink 核心元件 內部原理 多圖剖析元件
- STL vector的內部實現原理及基本用法
- 浮點數演算法的內部實現演算法
- java中的內部類Java
- 【譯】Go 切片:用法和內部實現Go
- 與你探索classnames模組內部實現
- 面試題 SDWebImage內部實現過程面試題Web
- Java中的匿名內部類及內部類的二三事Java
- 字典 dictionary c# 0104C#
- java 建構函式內部的多型方法 完全剖析Java函式多型
- C#中JSON字串和Dictionary字典型別的相互轉換C#JSON字串型別
- Xamarin iOS開發實戰中冊 (內部資料)C#蘋果應用開發iOSC#蘋果