Dictionary和hashtable用法有點類似,他們都是基於鍵值對的數據集合,但實際上他們內部的實現原理有很大的差別,java
先簡要概述一下他們主要的區別,稍後在分析Dictionary內部實現的大概原理。程序員
區別:1,Dictionary支持泛型,而Hashtable不支持。數組
2,Dictionary沒有裝填因子(Load Facto)概念,當容量不夠時才擴容(擴容跟Hashtable同樣,也是兩倍於當前容量最小素數),Hashtable是「已裝載元素」與」bucket數組長度「大於裝載因子時擴容。
ide
3,Dictionary內部的存儲value的數組按前後插入的順序排序,Hashtable不是。函數
4,當不發生碰撞時,查找Dictionary須要進行兩次索引定位,Hashtable需一次,。
性能
Dictionary採用除法散列法來計算存儲地址,想詳細瞭解的能夠百度一下,簡單來講就是其內部有兩個數組:buckets數組和entries數組(entries是一個Entry結構數組),entries有一個next用來模擬鏈表,該字段存儲一個int值,指向下一個存儲地址(實際就是bukets數組的索引),當沒有發生碰撞時,該字段爲-1,發生了碰撞則存儲一個int值,該值指向bukets數組.測試
下面跟上次同樣,按正常使用Dictionary時,看內部是如何實現的。spa
一,實例化一個Dictionary, Dictionary<string,string> dic=new Dictionary<string,string>();.net
a,調用Dictionary默認無參構造函數。pwa
b,初始化Dictionary內部數組容器:buckets int[]和entries<T,V>[],分別分配長度3。(內部有一個素數數組:3,7,11,17....如圖:);
二,向dic添加一個值,dic.add("a","abc");
a,將bucket數組和entries數組擴容3個長度。
b,計算"a"的哈希值,
c,而後與bucket數組長度(3)進行取模計算,假如結果爲:2
d,由於a是第一次寫入,則自動將a的值賦值到entriys[0]的key,同理將"abc"賦值給entriys[0].value,將上面b步驟的哈希值賦值給entriys[0].hashCode,
entriys[0].next 賦值爲-1,hashCode賦值b步驟計算出來的哈希值。
e,在bucket[2]存儲0。
三,經過key獲取對應的value, var v=dic["a"];
a, 先計算"a"的哈希值,假如結果爲2,
b,根據上一步驟結果,找到buckets數組索引爲2上的值,假如該值爲0.
c, 找到到entriys數組上索引爲0的key,
1),若是該key值和輸入的的「a」字符相同,則對應的value值就是須要查找的值。
2) ,若是該key值和輸入的"a"字符不相同,說明發生了碰撞,這時獲取對應的next值,根據next值定位buckets數組(buckets[next]),而後獲取對應buckets上存儲的值在定位到entriys數組上,......,一直到找到爲止。
3),若是該key值和輸入的"a"字符不相同而且對應的next值爲-1,則說明Dictionary不包含字符「a」。
Dictionary裏的其餘方法就不說了,各位能夠本身去看源碼,下面來經過實驗來對比Hashtable和Dictionary的添加和查找性能,
1,添加元素速度測評。
循環5次,每次內部在循環10次取平均值,PS:代碼中若有不公平的地方望各位指出,本人知錯就改。
a,值類型
static void Main(string[] args) { for (int i = 0; i < 5; i++) { Console.WriteLine(string.Format("第{0}次執行:", i + 1)); Add(); Console.WriteLine("-------華麗的分分隔線---------"); } Console.ReadKey(); } public static void Add() { Hashtable ht = new Hashtable(); Stopwatch st = new Stopwatch(); long ticks1 = 0; for (int j = 0; j < 10; j++) { st.Reset(); st.Start(); for (int i = 0; i < 1000000; i++) { ht.Add(i, i); } st.Stop(); ticks1 += st.ElapsedTicks; ht.Clear(); } Console.WriteLine(string.Format("Hashtable添加:{0}個元素,消耗:{1}", 1000000, ticks1 / 10)); Dictionary<int, int> dic = new Dictionary<int, int>(); ticks1 = 0; for (int j = 0; j < 10; j++) { st.Reset(); st.Start(); for (int i = 0; i < 1000000; i++) { dic.Add(i, i); } st.Stop(); ticks1 += st.ElapsedTicks; dic.Clear(); } Console.WriteLine(string.Format("Dictionary添加:{0}個元素,消耗:{1}", 1000000, st.ElapsedTicks)); }
結果:
經過運行結果來看,HashTable 速度明顯慢於Dictionary,相差一個數量級。我我的分析緣由可能爲:
a,Hashtable不支持泛型,我向你添加的int類型會發生裝箱操做,而Dictionary支持泛型。
b,Hashtable在擴容時會先new一個更大的數組,而後將原來的數據複製到新的數組裏,還需對新數組裏的key從新哈希計算(這多是最性能影響最大的因素)。而Dictionary不會這樣。
b,引用類型
static void Main(string[] args) { for (int i = 0; i < 5; i++) { Console.WriteLine(string.Format("第{0}次執行",i+1)); Add(); Console.WriteLine("--------華麗的分隔線------"); } Console.ReadKey(); } public static void Add() { Hashtable ht = new Hashtable(); Stopwatch st = new Stopwatch(); long ticks1 = 0; for (int j = 0; j < 10; j++) { st.Reset(); st.Start(); for (int i = 0; i < 1000000; i++) { ht.Add(i.ToString(), i.ToString()); } st.Stop(); ticks1 += st.ElapsedTicks; ht.Clear(); } Console.WriteLine(string.Format("Hashtable添加:{0}個元素,消耗:{1}", 1000000, ticks1 / 10)); Dictionary<string, string> dic = new Dictionary<string, string>(); ticks1 = 0; for (int j = 0; j < 10; j++) { st.Reset(); st.Start(); for (int i = 0; i < 1000000; i++) { dic.Add(i.ToString(), i.ToString()); } st.Stop(); ticks1 += st.ElapsedTicks; dic.Clear(); } Console.WriteLine(string.Format("Dictionary添加:{0}個元素,消耗:{1}", 1000000, st.ElapsedTicks)); }
Dic速度仍是比Hashtable快,但沒有值類型那麼明顯,這個測試可能有不許的地方。
2,查找速度測評(兩種狀況:值類型和引用類型)
1 值類型
static void Main(string[] args) { // GetByString(); GetByInt(); Console.ReadKey(); } public static void GetByInt() { //Hashtable Hashtable hs = new Hashtable(); Dictionary<int, int> dic = new Dictionary<int, int>(); for (int i = 0; i < 10000000; i++) { hs.Add(i, i); dic.Add(i, i); } long ticks = 0; Stopwatch st = new Stopwatch(); st.Reset(); for (int i = 0; i < 10; i++) { st.Start(); var result = hs[99999+i]; st.Stop(); ticks += st.ElapsedTicks; st.Reset(); } Console.WriteLine(string.Format("Hashtable查找10次,平均消耗:{0}", (float)ticks / 10)); //Dictionary ticks = 0; st.Reset(); for (int i = 0; i < 10; i++) { st.Start(); var result = dic[i]; st.Stop(); ticks += st.ElapsedTicks; st.Reset(); } Console.WriteLine(string.Format("Dictionary查找10次,平均消耗:{0}", (float)ticks / 10)); }
運行結果
2,引用類型
1 static void Main(string[] args) 2 { 3 GetByString(); 4 5 Console.ReadKey(); 6 } 7 8 public static void GetByString() 9 { 10 //Hashtable 11 Hashtable hs = new Hashtable(); 12 Dictionary<string, string> dic = new Dictionary<string, string>(); 13 14 for (int i = 0; i < 1000000; i++) 15 { 16 hs.Add(i.ToString(), i.ToString()); 17 dic.Add(i.ToString(), i.ToString()); 18 } 19 long ticks = 0; 20 Stopwatch st = new Stopwatch(); 21 st.Reset(); 22 string key = "9999"; 23 for (int i = 0; i < 10; i++) 24 { 25 st.Start(); 26 var result = hs[key]; 27 st.Stop(); 28 ticks += st.ElapsedTicks; 29 st.Reset(); 30 } 31 Console.WriteLine(string.Format("Hashtable查找10次,平均消耗:{0}", (float)ticks / 10)); 32 33 //Dictionary 34 ticks = 0; 35 st.Reset(); 36 for (int i = 0; i < 10; i++) 37 { 38 st.Start(); 39 var result = dic[key]; 40 st.Stop(); 41 ticks += st.ElapsedTicks; 42 st.Reset(); 43 } 44 Console.WriteLine(string.Format("Dictionary查找10次,平均消耗:{0}", (float)ticks / 10)); 45 }
運行結果
根據上面實驗結果能夠得出:
a,值類型,Hashtable和Dictionary性能相差不大,Hashtable稍微快於Dictionary.
b,引用類型:Hashtable速度明顯快於Dictionary。
PS:以上是我的不成熟觀點,若是錯誤請各位指出,謝謝,下篇介紹 SortedList 集合。
另:公司最近招聘.net和java程序員若干,如各位有找工做打算的,請發簡歷到wangjun@tonglukuaijian.com。
公司在上海閔行浦江智谷。