在本文中将从基础角度讲解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());得到线程安全的对象。
固然由于各自电脑的状况不同,可能会有部分偏差。若有问题,敬请斧正。