hashtable

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"的索引。

 

  1. Hashtable爲何查詢速度快,而添加速度慢,且其添加和查詢速度之比相差一個數量級?

Hashtable查詢速度快是由於內部是基於數組索引定位的,稍微消耗性能的是取key’的哈希值,添加性能慢是由於添加元素可能會地址衝突,須要從新定位地址,擴容後數組拷貝,從新計算key。

  1. 裝填因子(loadfactor)是什麼,hashtable默認的裝填因子時多少?

裝填因子是hashtable中已存元素數/內部bucket數組長度,這個比值太大會形成衝突機率增大,過小會形成空間浪費,默認是0.72.

  1. Hashtable裏的元素是順序排序的嗎?

不是順序的

  1. Hashtable內部的數據桶默認長度是多少,其長度爲何只能是素數?

默認長度是3,由於素數有一個特色,只能被本身和1整除,若是不是素數,那麼取模運算時可能會出現多個值。

相關文章
相關標籤/搜索