dictionary和hashtable用法有點相似,他們都是基於鍵值對的資料集合,但實際上他們內部的實現原理有很大的差異,
先簡要概述一下他們主要的區別,稍後在分析dictionary內部實現的大概原理。
區別:1,dictionary支援泛型,而hashtable不支援。
2,dictionary沒有裝填因子(load facto)概念,當容量不夠時才擴容(擴容跟hashtable一樣,也是兩倍於當前容量最小素數),hashtable是「已裝載元素」與」bucket陣列長度「大於裝載因子時擴容。
3,dictionary內部的儲存value的陣列按先後插入的順序排序,hashtable不是。
4,當不發生碰撞時,查詢dictionary需要進行兩次索引定位,hashtable需一次,。
dictionary採用除法雜湊法來計算儲存位址,想詳細了解的可以百度一下,簡單來說就是其內部有兩個陣列:buckets陣列和entries陣列(entries是乙個entry結構陣列),entries有乙個next用來模擬鍊錶,該欄位儲存乙個int值,指向下乙個儲存位址(實際就是bukets陣列的索引),當沒有發生碰撞時,該字段為-1,發生了碰撞則儲存乙個int值,該值指向bukets陣列.
下面跟上次一樣,按正常使用dictionary時,看內部是如何實現的。
一,例項化乙個dictionary, dictionary dic=new dictionary();
a,呼叫dictionary預設無參建構函式。
b,初始化dictionary內部陣列容器:buckets int和entries,分別分配長度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:**中如有不公平的地方望各位指出,本人知錯就改。
**:
複製**
static void main(string args)
次執行:", i + 1));
add();
console.writeline("-------華麗的分分隔線---------");
}console.readkey();
}public static void add()
st.stop();
ticks1 += st.elapsedticks;
ht.clear();
}console.writeline(string.format("hashtable新增:個元素,消耗:", 1000000, ticks1 / 10));
dictionary dic = new dictionary();
ticks1 = 0;
for (int j = 0; j < 10; j++)
st.stop();
ticks1 += st.elapsedticks;
dic.clear();
}console.writeline(string.format("dictionary新增:個元素,消耗:", 1000000, st.elapsedticks));
}複製**
結果:
通過執行結果來看,hashtable 速度明顯慢於dictionary,相差乙個數量級。我個人分析原因可能為:
a,hashtable不支援泛型,我向你新增的int型別會發生裝箱操作,而dictionary支援泛型。
b,hashtable在擴容時會先new乙個更大的陣列,然後將原來的資料複製到新的陣列裡,還需對新陣列裡的key重新雜湊計算(這可能是最效能影響最大的因素)。而dictionary不會這樣。
2,查詢速度測評(兩種情況:值型別和引用型別)
1 值型別
複製**
static void main(string args)
public static void getbyint()
long ticks = 0;
stopwatch st = new stopwatch();
st.reset();
for (int i = 0; i < 10; i++)
console.writeline(string.format("hashtable查詢10次,平均消耗:", (float)ticks / 10));
//dictionary
ticks = 0;
st.reset();
for (int i = 0; i < 10; i++)
console.writeline(string.format("dictionary查詢10次,平均消耗:", (float)ticks / 10));
}複製**
執行結果
2,引用型別
複製**
1 static void main(string args)
2
7 8 public static void getbystring()
9
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
31 console.writeline(string.format("hashtable查詢10次,平均消耗:", (float)ticks / 10));
32 33 //dictionary
34 ticks = 0;
35 st.reset();
36 for (int i = 0; i < 10; i++)
37
44 console.writeline(string.format("dictionary查詢10次,平均消耗:", (float)ticks / 10));
45 }
複製**
執行結果
根據上面實驗結果可以得出:
a,值型別,hashtable和dictionary效能相差不大,hashtable稍微快於dictionary.
b,引用型別:hashtable速度明顯快於dictionary。
Android核心剖析之Window內部機制
windowmanager其實指示乙個介面,其具體實現是在windowmanagerimpl,而windowmanagerimpl其實什麼都沒做,指示呼叫windowmanagerglobal中的方法來實現的,windowmanagerglobal是個單例模式,通過win。windowmanager...
從彙編層面深度剖析C 虛函式
虛函式是c 語言實現執行時多型的唯一手段,因此掌握c 虛函式也成為c 程式設計師是否合格的試金石。csdn所發的一篇博文 vc虛函式布局引發的問題 從彙編角度分析了物件虛函式表的構,以及c 指標或者引用是如何利用這個表來實現執行時多型。誠然,c 虛函式的結構會因編譯器不同而異,但所使用的原理是一樣的...
C 之集合概述
集合簡單的說就是陣列的公升級版。他可以動態的對集合的長度 也就是集合內最大元素的個數 進行定義和維護!arraylist非常類似於陣列。也有人稱他為陣列列表,它的容量可以根據需要進行動態擴充,而它的索引也會根據集合容量的擴充套件而重新分配和調整。也就是說arraylist集合中的元素的下標是不確定的...