在本文中將從基礎角度講解HashTable、Dictionary的構造和經過程序進行插入讀取對比。html
一:HashTable算法
1.HashTable是一種散列表,他內部維護不少對Key-Value鍵值對,其還有一個相似索引的值叫作散列值(HashCode),它是根據GetHashCode方法對Key經過必定算法獲取獲得的,全部的查找操做定位操做都是基於散列值來實現找到對應的Key和Value值的。c#
2.咱們須要使用一個算法讓散列值對應HashTable的空間地址儘可能不重複,這就是散列函數(GetHashCode)須要作的事。安全
3.當一個HashTable被佔用一大半的時候咱們經過計算散列值取得的地址值可能會重複指向同一地址,這就是哈希衝突。數據結構
在.Net中鍵值對在HashTable中的位置Position= (HashCode& 0x7FFFFFFF) % HashTable.Length,.net中是經過探測法解決哈希衝突的,當經過散列值取得的位置Postion以及被佔用的時候,就會增長一個位移x值判斷下一個位置Postion+x是否被佔用,若是仍然被佔用就繼續往下位移x判斷Position+2*x位置是否被佔用,若是沒有被佔用則將值放入其中。當HashTable中的可用空間愈來愈小時,則獲取獲得可用空間的難度愈來愈大,消耗的時間就越多。多線程
4.當前HashTable中的被佔用空間達到一個百分比的時候就將該空間自動擴容,在.net中這個百分比是72%,也叫.net中HashTable的填充因子爲0.72。例若有一個HashTable的空間大小是100,當它須要添加第73個值的時候將會擴容此HashTable.函數
5.這個自動擴容的大小是多少呢?答案是當前空間大小的兩倍最接近的素數,例如當前HashTable所佔空間爲素數71,若是擴容,則擴容大小爲素數131.post
二:Dictionary大數據
1.Dictionary是一種變種的HashTable,它採用一種分離連接散列表的數據結構來解決哈希衝突的問題。ui
2.分離連接散列表是當散列到同一個地址的值存爲一個鏈表中。
3.這個變種HashTable的填充因子是1
三:本文將以代碼的形式探索HashTable和Dictionary的插入和三種讀取方式的效率(for/foreach/GetEnumerator)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
|
public
class
HashTableTest
{
static
Hashtable _Hashtable;
static
Dictionary<
string
,
object
> _Dictionary;
static
void
Main()
{
Compare(10);
Compare(10000);
Compare(5000000);
Console.ReadLine();
}
public
static
void
Compare(
int
dataCount)
{
Console.WriteLine(
"-------------------------------------------------n"
);
_Hashtable =
new
Hashtable();
_Dictionary =
new
Dictionary<
string
,
object
>();
Stopwatch stopWatch =
new
Stopwatch();
//HashTable插入dataCount條數據須要時間
stopWatch.Start();
for
(
int
i = 0; i < dataCount; i++)
{
_Hashtable.Add(
"Str"
+ i.ToString(),
"Value"
);
}
stopWatch.Stop();
Console.WriteLine(
" HashTable插入"
+ dataCount +
"條數據須要時間:"
+ stopWatch.Elapsed);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
stopWatch.Start();
for
(
int
i = 0; i < dataCount; i++)
{
_Dictionary.Add(
"Str"
+ i.ToString(),
"Value"
);
}
stopWatch.Stop();
Console.WriteLine(
" Dictionary插入"
+ dataCount +
"條數據須要時間:"
+ stopWatch.Elapsed);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
int
si = 0;
stopWatch.Start();
for
(
int
i=0;i<_Hashtable.Count;i++)
{
si++;
}
stopWatch.Stop();
Console.WriteLine(
" HashTable遍歷時間:"
+ stopWatch.Elapsed +
" ,遍歷採用for方式"
);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
si = 0;
stopWatch.Start();
foreach
(var s
in
_Hashtable)
{
si++;
}
stopWatch.Stop();
Console.WriteLine(
" HashTable遍歷時間:"
+ stopWatch.Elapsed +
" ,遍歷採用foreach方式"
);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
si = 0;
stopWatch.Start();
IDictionaryEnumerator _hashEnum = _Hashtable.GetEnumerator();
while
(_hashEnum.MoveNext())
{
si++;
}
stopWatch.Stop();
Console.WriteLine(
" HashTable遍歷時間:"
+ stopWatch.Elapsed +
" ,遍歷採用HashTable.GetEnumerator()方式"
);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
si = 0;
stopWatch.Start();
for
(
int
i=0;i<_Dictionary.Count;i++)
{
si++;
}
stopWatch.Stop();
Console.WriteLine(
" Dictionary遍歷時間:"
+ stopWatch.Elapsed +
" ,遍歷採用for方式"
);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
si = 0;
stopWatch.Start();
foreach
(var s
in
_Dictionary)
{
si++;
}
stopWatch.Stop();
Console.WriteLine(
" Dictionary遍歷時間:"
+ stopWatch.Elapsed +
" ,遍歷採用foreach方式"
);
//Dictionary插入dataCount條數據須要時間
stopWatch.Reset();
si = 0;
stopWatch.Start();
_hashEnum = _Dictionary.GetEnumerator();
while
(_hashEnum.MoveNext())
{
si++;
}
stopWatch.Stop();
Console.WriteLine(
" Dictionary遍歷時間:"
+ stopWatch.Elapsed +
" ,遍歷採用Dictionary.GetEnumerator()方式"
);
Console.WriteLine(
"n-------------------------------------------------"
);
}
}
|
四:從上面的結果能夠看出
1.HashTable大數據量插入數據時須要花費比Dictionary大的多的時間。
2.for方式遍歷HashTable和Dictionary速度最快。
3.在foreach方式遍歷時Dictionary遍歷速度更快。
五:在單線程的時候使用Dictionary更好一些,多線程的時候使用HashTable更好。
由於HashTable能夠經過Hashtable tab = Hashtable.Synchronized(new Hashtable());得到線程安全的對象。
固然由於各自電腦的狀況不同,可能會有部分偏差。若有問題,敬請斧正。