從內部剖析C 集合之

2021-12-29 21:44:37 字數 4063 閱讀 3413

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集合中的元素的下標是不確定的...