C# Hashtable、HashSet和Dictionary的区别
1.Hashtable
哈希表(HashTable)表示键/值对的集合。在.NET Framework中,Hashtable是System.Collections命名空间提供的一个容器,用于处理和表现类似key-value的键值对,其中key通常可用来快速查找,同时key是区分大小写;value用于存储对应于key的值。Hashtable中key-value键值对均为object类型,所以Hashtable可以支持任何类型的keyvalue键值对,任何非 null 对象都可以用作键或值。
2.HashSet类
主要是设计用来做高性能集运算的,例如对两个集合求交集、并集、差集等。集合中包含一组不重复出现且无特性顺序的元素,HashSet拒绝接受重复的对象。
HashSet的一些特性如下:
a. HashSet中的值不能重复且没有顺序。
b. HashSet的容量会按需自动添加。
3.Dictionary
他本身有集合的功能有时候可以把它看成数组
他的结构是这样的:Dictionary<[key], [value]>
他的特点是存入对象是需要与[key]值一一对应的存入该泛型
通过某一个一定的[key]去找到对应的值
Hashtable 与 Dictionary
第一、存储的数据类型
Hashtable不是泛型的,不是类型安全的;Dictionary是泛型的,是类型安全的;
Hashtable的键值都是Object类型的,但是Dictionary的键值的数据类型是可以指定的。
也就是说如果往Hashtable里面存入Object以外的数据类型,则在取出该数据时,需要对其进行显示的类型转换,才能够正常使用。而Dictionary则没有这个问题。
从这方面讲的话,Hashtable相当于Dictionary<Object,Object>
Hashtable ht = new Hashtable();
Dictionary<string, int> dic = new Dictionary<string, int>();
ht.Add("A", 1);
dic.Add("A", 1);
//Console.WriteLine(ht["A"]+1); //编译错误!Object类型不能和int类型直接进行相加。
Console.WriteLine((int)ht["A"] + 1);//编译通过,输出结果为:2
Console.WriteLine(dic["A"] + 1);
第二、读取数据的顺序与添加数据的顺序的一致性
Dictionary和Hashtable的读取数据的顺序和添加数据时的数据的顺序的一致性均不能够保证,或者可以说没有一致性。
Dictionary在只添加不删除的时候能够保持读取数据的顺序和添加时候的顺序是一致的;但是经过删除和添加操作之后,就不能够保证读取数据的顺序和添加时候的顺序一致了。
Dictionary<int, int> dic = new Dictionary<int, int>();
dic.Add(0, 0);
dic.Add(1, 1);
dic.Add(2, 2);
Console.WriteLine("仅仅经过添加元素处理之后:");
foreach (KeyValuePair<int, int> kvp in dic)
{
Console.WriteLine("Key:" + kvp.Key + " Value:" + kvp.Value);
}
dic.Remove(0);
dic.Add(3, 3);
Console.WriteLine("经过删除和添加元素处理之后:");
foreach (KeyValuePair<int, int> kvp in dic)
{
Console.WriteLine("Key:" + kvp.Key + " Value:" + kvp.Value);
}
对于Dicitionary而言,如果从中删除一个元素,则之后新添加的元素会填补这个被删除元素的位置,因而致使添加数据的顺序与读取数据的顺序是不一致的。
对于Hashtable而言,它的数据存储顺序是按一定的算法算出来的,所以绝大多数情况下,它的数据读取顺序和数据添加顺序是不一致的。
所以如果你需要保持数据添加时的顺序的时候,最好不要用Dictionary和Hashtable。
第三、当用一个不存在的Key值到Hashtable或者Dictionary中取值时
对于Hashtable而言,如果用一个不存在的Key值进行取值的话,会返回一个null;
Hashtable ht = new Hashtable();
Console.WriteLine(ht["b"]==null);
对于Dictionary而言,如果用一个不存在的Key值进行取值的话,会引发“System.Collections.Generic.KeyNotFoundException”类型的异常。
所以在从Dictionary或者Hashtable取值时,可以先判断Key值是否存在(用ContainsKey()方法进行判断),以防止出现预期以外的值或者异常。
第四、线程安全性
Dictionary不是线程安全的,Hashtable是线程安全的。对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减。
HashSet代替List问题
NET3.5多了个HasSet<T>用来存储集合。从名称可以看出,它是基于Hash的。可以简单理解为没有Value的Dictionary<TKey,TValue>。
HashSet<T>不能用索引访问,不能存储重复数据,元素T必须正确实现了Equals和GetHashCode。
那它的优势是什么呢?
检索的性能。简单的说它的Contains方法的性能在大数据量时比List<T>好得多。HashSet<T>的Contains方法复杂度是O(1),List<T>的Contains方法复杂度是O(n)。
那么,在集合的目的是为了检索的情况下,我们应该使用HashSet<T>代替List<T>。比如一个存储关键字的集合,运行的时候通过其Contains方法检查输入字符串是否关键字。
HashSet<T>是专门设计用来做集合运算(取交集,并集等),所以提供了UnionWith、IntersectWith等方法。
另:如果数据量很小,那么任然推荐使用List<T>。
这个“小”是多小呢?其实是用Hashtable还是ListDictionary时存在同样的取舍问题,.NET为其设计了HybridDictionary类实现一个混合容器,当数量小于等于8(目前是8,不保证微软以后不会变)的时候,HybridDictionary内部使用ListDictionary,当数量大于8的时候,HybridDictionary内部使用Hashtable。所以,如果我们知道我们集合的数量不会大于8的话,就算目的是为了检索,任然推荐使用List<T>。
由于 Hashtable 和 Dictionary 同时存在, 在使用场景上必然存在选择性, 并不任何时刻都能相互替代.
[1] 单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分.
[2] 多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用 Synchronized() 方法可以获得完全线程安全的类型. 而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护, 效率大减.
[3] Dictionary 有按插入顺序排列数据的特性 (注: 但当调用 Remove() 删除过节点后顺序被打乱), 因此在需要体现顺序的情境中使用 Dictionary 能获得一定方便.