首页 > 编程语言 >C#数据结构--Dictionary、HashTable、List、HashSet区别

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

时间:2022-11-29 20:36:34浏览次数:59  
标签:Dictionary HashSet C# List HashTable 散列 Hashtable


在.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区别_HashTable

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

插入性能: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方法检查输入字符串是否关键字。

标签:Dictionary,HashSet,C#,List,HashTable,散列,Hashtable
From: https://blog.51cto.com/u_6871414/5896975

相关文章

  • C#设计模式读书笔记之设计模式的设计原则
    设计模式的设计原则:(重要性从上往下排列)开闭原则:对扩展开放,对修改关闭依赖倒转原则:高层模块不应该依赖底层模块,它们都应该依赖抽象;要针对抽象层编程,而不要针对具体类编程。......
  • Unity-Animator Override Controller
    AnimatorOverrideController是一种资产类型,允许您扩展现有的AnimatorController,替换使用的特定动画,但保留原始结构,参数和逻辑。允许您创建相同基本状态机的多个变体,但每......
  • ClassLoader
    ClassLoader:用来加载Class的。负责将Class的字节码形式转换成内存形式的Class对象。字节码可以来自于磁盘文件*.class,也可以是jar包里的*.class,也可以来自远程......
  • C++函数编译原理和成员函数的实现
    对象的内存中只保留了成员变量,除此之外没有任何其他信息,程序运行时不知道stu的类型为Student,也不知道它还有四个成员函数setname()、setage()、setscore()、show(),C++......
  • sizeof(struct)和sizeof(union)的结果分析及其原因
    一个错误有的时候,在脑海中停顿了很久的“显而易见”的东西,其实根本上就是错误的。就拿下面的问题来看:structT{charch;inti;};使用sizeof(T),将得到什么样的答案呢......
  • leetcode 1976.到达目的地的方案数
    问题描述:你在一个城市里,城市由n 个路口组成,路口编号为 0 到 n-1 ,某些路口之间有双向 道路。输入保证你可以从任意路口出发到达其他任意路口,且任意两个路口之间......
  • C++多态性
    虚函数是C++中用于实现多态(polymorphism)的机制。核心理念就是通过基类访问派生类定义的函数。虚函数    是在基类中使用关键字virtual声明的函数。在派生类中重......
  • C#数据结构-Dictionary实现
    在看过一篇大神写的​​《带你看懂Dictionary的内部实现》​​,对Dictionary的内部实现有了一个清晰的了解,但纸上得来终觉浅,作为程序员,还是自己调试一下代码,记忆更深刻,为此专......
  • C#数据结构-红黑树实现
    二叉查找树,他对于大多数情况下的查找和插入在效率上来说是没有问题的,但是他在最差的情况下效率比较低。红黑树保证在最坏的情况下插入和查找效率都能保证在对数的时间复杂度......
  • C#数据结构-有限状态机
    有限状态机FSM的要点是:  拥有一组状态,并且可以在这组状态之间进行切换。  状态机同时只能在一个状态。  一连串的输入或事件被发送给机器。  每个状态都......