在.Net  模仿java 的过程中,抛弃了 HashMap,所以我们今天分析下Dictionary、HashTable、HashSet区别。

处理碰撞,即碰撞到同一个Bucket槽上:

Hashtable和Dictionary从数据结构上来说都属于Hashtable(哈希表),都是对关键字(键值)进行散列操作,将关键字散列到Hashtable的某一个槽位中去,不同的是处理碰撞的方法。散列函数有可能将不同的关键字散列到Hashtable中的同一个槽中去,这个时候我们称发生了碰撞,为了将数据插入进去,我们需要另外的方法来解决这个问题。

Dictionary采用链表法处理碰撞:

int hashCode = comparer.GetHashCode(key) & 0x7FFFFFFF;
//将HashCode的返回值转化为数组索引
int bucketIndex = hashCode % buckets.Length;

通过Hash算法来碰撞到指定的Bucket上,碰撞到同一个Bucket槽上所有数据形成一个单链表。

HashTable采用开放寻址法方法中的双重散列处理碰撞:

双重散列:

h(k,i) = (h1(k) +i*h2(k)) mod m  (其中i = 0,1,…,m-1),其中h1和h2为辅助散列函数。初始探查位置为T(h1(k)), 后续的探查位置在此基础上加上偏移量h2(k)模m 。

取m为根据构造函数设定的初始容量,获取一个大于并接近的素数,并取h1(k) = k mod m, h2(k) = 1 + (k mod (m - 1))

适用场景:

C#数据结构--Dictionary、HashTable、List、HashSet区别_List

经过测试,测试数据会有波动性,但基本能反应整体情况:

插入性能:List < HashTable < Dictionary < LinkedList

遍历性能:HashTable < Dictionary < LinkedList < List

删除性能:List < HashTable < LinkedList < Dictionary

经过测试,对于值类型(不包括 Object)的 Dictionary<TKey, TValue> 的性能优于 Hashtable,所以推荐使用Dictionary。

Dictionary和HashTable的区别

 1:单线程程序中推荐使用 Dictionary, 有泛型优势, 且读取速度较快, 容量利用更充分。
 2:多线程程序中推荐使用 Hashtable, 默认的 Hashtable 允许单线程写入, 多线程读取, 对 Hashtable 进一步调用  Synchronized() 方法可以获得完全线程安全的类型;而 Dictionary 非线程安全, 必须人为使用 lock 语句进行保护,  效率大减.
 3:对于值类型,特定类型(不包括 Object)的 Dictionary<TKey, TValue> 的性能优于 Hashtable,这是因为 Hashtable 的元素属于 Object  类型,所以在存储或检索值类型时通常发生装箱和拆箱操作。

Dictionary和HashTable的相同点
Hashtable 类和 Dictionary<TKey, TValue> 泛型类实现 IDictionary 接口 
Dictionary<TKey, TValue> 泛型类还实现 IDictionary<TKey, TValue>泛型接口。
因此,这些集合中的每个元素都是一个键/值对。

Dictionary<TKey, TValue> 类与 Hashtable 类的功能相同。

Dictionary、HashTable和List区别

我们清楚List<T>是对数组做了一层包装,我们在数据结构上称之为线性表,而线性表的概念是,在内存中的连续区域,除了首节点和尾节点外,每个节点都有着其唯一的前驱结点和后续节点。我们在这里关注的是连续这个概念。

而HashTable、Dictionary,是根据Key而根据Hash算法分析产生的内存地址,因此在宏观上是不连续的,虽然微软对其算法也进行了很大的优化。

由于这样的不连续,在遍历时,Dictionary必然会产生大量的内存换页操作,而List只需要进行最少的内存换页即可,这就是List和Dictionary在遍历时效率差异的根本原因。

所以根据Key的查找Dictionary、HashTable的效率是高于 List 的, 但是遍历的话则List效率更好。

HashSet

先来了解下HashSet<T>类,主要被设计用来存储集合,做高性能集运算,例如两个集合求交集、并集、差集等。从名称可以看出,它是基于Hash的,可以简单理解为没有Value的Dictionary。

HashSet<T>不能用索引访问,不能存储重复数据。

HashSet<T>和与List<T>的比较
HashSet<T>最大的优势是检索的性能,简单的说它的Contains方法的性能在大数据量时比List<T>好得多。曾经做过一个测试,将800W条int类型放在List<int>集合中,使用Contains判断是否存在,速度巨慢,而放在HashSet<int>性能得到大幅提升。

在内部算法实现上,HashSet<T>的Contains方法复杂度是O(1),List<T>的Contains方法复杂度是O(n),后者数据量越大速度越慢,而HashSet<T>不受数据量的影响。

所以在集合的目的是为了检索的情况下,我们应该使用HashSet<T>代替​List<T>​。比如一个存储关键字的集合,运行的时候通过其Contains方法检查输入字符串是否关键字。