Hashtable算法
internal const Int32 HashPrime = 101;//一個固定的素數數組
private const Int32 InitialSize = 3;//哈希表的默認容量函數
private struct bucket {性能
public Object key;//鍵排序
public Object val;//值索引
public int hash_col;//哈希碼,最高位是符號位,0:正整數,表示未發生衝突,1:負整數,表示發生衝突,存儲的是h(key, i)的值而不是哈希地址ci
}hash
private bucket[] buckets;it
private int count;//元素總數io
private int occupancy;//衝突次數
private int loadsize;//至關於一個閾值,達到了這個數值,將對哈希表進行擴容
private float loadFactor;//哈希表中的元素佔有數據桶空間的一個比率,這個值直接決定了哈希表在何時進行擴容。
private volatile int version;
private volatile bool isWriterInProgress;
private ICollection keys;
private ICollection values;
在Hashtable中的兩個哈希函數分別爲:
1. h_1(k) = k.GetHashCode():第一個哈希函數直接用默認的GetHashCode()方法;
2. h_2(k) = (1 + ((h_1(k) * HashPrime) % (hashsize - 1))):HashPrime爲私有成員101的素數,hashsize爲哈希表長度。之因此會進行取模運算是爲了保證結果值的範圍在[0, hashsize - 1]。
構建哈希算法的函數
Hashtable解決衝突使用了雙重散列法,但又跟前面所講的雙重散列法稍有不一樣。它探測地址的方法以下:
h(key, i) = h1(key) + i * h2(key)
其中哈希函數h1和h2的公式以下:
h1(key) = key.GetHashCode()
h2(key) = 1 + (((h1(key) >> 5) + 1) % (hashsize - 1))
因爲使用了二度哈希,最終的h(key, i)的值有可能會大於hashsize,因此須要對h(key, i)進行模運算,最終計算的哈希地址爲:
哈希地址 = h(key, i) % hashsize
哈希表經過關鍵字查找元素時,首先計算出鍵的哈希地址,而後經過這個哈希地址直接訪問數組的相應位置並對比兩個鍵值,若是相同,則查找成功並返回;若是不一樣,則根據hash_coll的值來決定下一步操做。當hash_coll爲0或正數時,代表沒有衝突,此時查找失敗;若是hash_coll爲負數時,代表存在衝突,此時需經過二度哈希繼續計算哈希地址進行查找,如此反覆直到找到相應的鍵值代表查找成功,若是在查找過程當中遇到hash_coll爲正數或計算二度哈希的次數等於哈希表長度則查找失敗。由此可知,將hash_coll的高位設爲衝突位主要是爲了提升查找速度,避免無心義地屢次計算二度哈希的狀況。
一,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查詢速度快是由於內部是基於數組索引定位的,稍微消耗性能的是取key’的哈希值,添加性能慢是由於添加元素可能會地址衝突,須要從新定位地址,擴容後數組拷貝,從新計算key。
裝填因子是hashtable中已存元素數/內部bucket數組長度,這個比值太大會形成衝突機率增大,過小會形成空間浪費,默認是0.72.
不是順序的
默認長度是3,由於素數有一個特色,只能被本身和1整除,若是不是素數,那麼取模運算時可能會出現多個值。