從內部剖析C#集合之HashTable

  計劃寫幾篇文章專門介紹HashTable,Dictionary,HashSet,SortedList,List 等集合對象,從內部剖析原理,以便在實際應用中有針對性的選擇使用。 這篇文章先介紹HashTable .
  先例舉幾個問題:1,Hashtable爲何速度查詢速度快,而添加速度相對慢,且其添加和查詢速度之比相差一個數量等級?
  2,裝填因子( Load Factor)是什麼,hashtable默認的裝填因子是多少?
  3,hashtable裏的元素是順序排序的嗎?
  4,hashtable內部的數據桶(數組)默認長度多少,其長度爲何只能是素數?
  Hashtable中的數據實際存儲在內部的一個數據桶裏(bucket結構數組),其和普通的數組同樣,容量固定,根據數組索引獲取值。
  下面從正常使用Hashtable場景看內部是如何實現的,內部都作了哪些工做。
  一,new一個Hashtable,Hashtable ht=new Hashtable();
  Hashtable有多個構造函數,經常使用的是無參構造函數:Hashtable ht=new Hashtable(),在new一個hashtable時,其內部作了以下工做:調用Hashtable(int capacity,float loadFactor),其中capacity爲:0,loadFactor爲:1,而後初始化bocket數組大小爲3,裝載因子爲0.72(該值是微軟權衡後給的值),以下圖所示,該圖截取Reflector
  二,向Hashtable添加一個元素,ht.Add("a","123")
  1,判斷當前Hashtable :ht的元素個數與bucket數組之比是否超過裝載因子0.72,
  1)小於0.72:對a進行哈希取值,而後將獲得的值與bucket數組長度進行取模計算,將取模的結果插入到bucket數組對應索引,將"123"賦值其value.
  由於哈希值可能會重複(不明白的百度一下),從而致使地址衝突,Hashtable 採用的是 "開放定址法" 處理衝突, 具體行爲是把 HashOf(k) % Array.Length 改成 (HashOf(k) + d(k)) % Array.Length , 得出另一個位置來存儲關鍵字 "a" 所對應的數據, d 是一個增量函數。 若是仍然衝突, 則再次進行增量, 依此循環直到找到一個 Array 中的空位爲止。
  2) 大於0.72:對bucket數組進行擴容,a, 新建一個數組(該數組的大小爲兩倍於當前容量最小的素數,好比當前數組長度是3,那麼新數組長度爲7)。
  b,將原來數組元素拷貝到新的數組中,由於bocket數組長度變了,因此須要對全部key從新哈希計算(這是影響hashtable性能的重要因素)。
  c, 進行上面a步驟。
  三,經過key獲取Hashtable對應的value,var v=ht["a"];
  1) 計算"a"的哈希值。
  2)將計算的結果與bocket數組長度進行取模計算,由於哈希值可能會衝突,因此相似定位索引上的key可能與輸入的key不相同,這時繼續查找下一個位置……
  3)取模計算結果便是存儲在bocket數組上"123"的索引。
  Hashtable還有不少方法,好比Clear ,Remove ,ContainsKey,ContainsValue等方法,因篇幅有限這裏就不一一介紹了。
  寫到這裏來回答一下篇幅開頭的幾個問題。
  1,Hashtable查詢速度快是由於內部是基於數組索引定位的,稍微消耗性能的是取KEY的哈希值,添加性能相對查詢慢是由於:a,添加元素時可能會地址衝突,須要從新定位地址 . b,擴容後 數組拷貝,從新哈希計算舊數組全部key託福答案
  2, 裝填因子是Hashtable"已存元素個數/內部bucket數組長度",這個比值太大會形成衝突機率增大,過小會形成空間的浪費。默認是0.72,該值是微軟通過大量實驗得出的一個比較平衡的值,裝填因子範圍 0.1<loadFactor<1,不然拋出ArgumentOutOfRangeException異常。
  3,不是順序的(各位看了文章應該知道爲何不是順序的了吧?)
  4,默認長度是3,我看的是。net framework 4.5版本反編譯的代碼,其餘版本的。net framework不肯定是否是這個值。爲何擴容的數組長度必定要是素數呢?由於素數有一個特色,只能被本身和1整除,若是不是素數那麼在進行取模計算的時候可能會出現多個值。數組

相關文章
相關標籤/搜索