C#集合类型总结和性能分析

C#集合类型概述

集合是.NET FCL(Framework Class Library)中很重要的一部分。所有的集合类都继承自IEnumerable。集合类总体可分为一下几类:关联/非关联型集合,顺序/随机访问集合,顺序/无序集合,泛型/非泛型集合,线程安全集合。

各集合类底层接口关系图

这里写图片描述

泛型与非泛型集合类的分析

  1. 泛型集合是类型安全的,基于固定的泛型T,运行时不需要像非泛型的执行Object和具体类型的类型转换。
  2. 泛型集合的效率相对较高。
  3. 两者都能实现数据存储,不同的是泛型只能存放T类型数据,有运行时检测,而非泛型的都转化为Object存储,能存储任意类型,包括值类型,会带来装箱拆箱的性能损耗,同时都是Object类型(弱类型)编译时无法类型检测,运行时会导致类型不一致的安全性问题。

具体接口/类分析

CollectionBase/DictionaryBase的目的

  1. 都是抽象类,不能实例化;
  2. 目的是提供给用户自定义实现强类型的集合,解决一般非泛型集合的弱类型不安全的问题。

IEnumerator/IEnumerable

IEnumerator定义了我们遍历集合的基本方法,以便我们可以实现单向向前的访问集合中的每一个元素。 所有的集合类都继承了IEnumerator接口,包括String类。而IEnumerable只有一个方法GetEnumerator即得到遍历器。

ICollection<T>和ICollection

从最上面第一张图我们可以知道,ICollection是直接继承自IEnumerable。而实际上也是如此,我们可以说ICollection比IEnumerable多支持一些功能,不仅仅只提供基本的遍历功能,还包括:

统计集合和元素个数 
获取元素的下标 
判断是否存在 
添加元素到未尾 
移除元素等等。。。 
ICollection 与ICollection<T> 略有不同,ICollection不提供编辑集合的功能,即Add和Remove。包括检查元素是否存在Contains也不支持。

IList和IList

IList则是直接继承自ICollection和IEnumerable。所以它包括两者的功能,并且支持根据下标访问和添加元素。IndexOf, Insert, RemoveAt等等。我们可以这样说,IEnumerable支持的功能最少,只有遍历。而ICollection支持的功能稍微多一点,不仅有遍历还有维护这个集合的功能。而IList是最全的版本。

IReadOnlyList<T>

这个是在Framework4.5中新增的接口类型,可以被看作是IList的缩减版,去掉了所有可能更改这个集合的功能。比如:Add, RemoveAt等等。

IDictionary<TKey,TValue>

IDictionary提供了对键值对集合的访问,也是继承了ICollection<T>和IEnumerable,扩展了通过Key来访问和操作数据的方法。


关联性泛型集合类

关联性集合类即我们常说的键值对集合,允许我们通过Key来访问和维护集合。我们先来看一下 FCL为我们提供了哪些泛型的关联性集合类:

Dictionary <TKey,TValue> 
SortedDictionary<TKey,TValue> 
SortedList<TKey,TValue> 
Dictionary<TKey,TValue>

Dictionary<TKey,TValue>

是我们最常用的关联性集合了,它的访问,添加,删除数据所花费的时间是所有集合类里面最快的,因为它内部用了Hashtable作为存储结构,所以不管存储了多少键值对,查询/添加/删除所花费的时间都是一样的,它的时间复杂度是O(1)。

Dictionary<TKey,TValue>优势是查找插入速度快,那么什么是它的劣势呢?因为采用Hashtable作为存储结构,就意味着里面的数据是无序排列的,所以想按一定的顺序去遍历Dictionary<TKey,TValue>里面的数据是要费一点工夫的。

作为TKey的类型必须实现GetHashCode()和Equals() 或者提供一个IEqualityComparer,否则操作可能会出现问题。

SortedDictioanry<TKey,TValue>

SortedDictionary<TKey,TValue>和Dictionary<TKey,TValue>大致上是类似的,但是在实现方式上有一点点区别。SortedDictionary<TKey,TValue>用二叉树作为存储结构的。并且按key的顺序排列。那么这样的话SortedDictionary<TKey,TValue>的TKey就必须要实现IComparable<TKey>。如果想要快速查询的同时又能很好的支持排序的话,那就使用SortedDictionary吧。

SortedList<TKey,TValue>

SortedList<TKey,TValue>是另一个支持排序的关联性集合。但是不同的地方在于,SortedList实际是将数据存存储在数组中的。也就是说添加和移除操作都是线性的,时间复杂度是O(n),因为操作其中的元素可能导致所有的数据移动。但是因为在查找的时候利用了二分搜索,所以查找的性能会好一些,时间复杂度是O(log n)。所以推荐使用场景是这样地:如果你想要快速查找,又想集合按照key的顺序排列,最后这个集合的操作(添加和移除)比较少的话,就是SortedList了。


非关联性泛型集合类

非关联性集合就是不用key操作的一些集合类,通常我们可以用元素本身或者下标来操作。FCL主要为我们提供了以下几种非关联性的泛型集合类。

List<T> 
LinkedList<T> 
HashSet<T> 
SortedSet<T> 
Stack<T> 
Queue<T> 
List<T>

泛型的List 类提供了不限制长度的集合类型,List在内部维护了一定长度的数组(默认初始长度是4),当我们插入元素的长度超过4或者初始长度 的时候,会去重新创建一个新的数组,这个新数组的长度是初始长度的2倍(不永远是2倍,当发现不断的要扩充的时候,倍数会变大),然后把原来的数组拷贝过来。所以如果知道我们将要用这个集合装多少个元素的话,可以在创建的时候指定初始值,这样就避免了重复的创建新数组和拷贝值。

另外的话由于内部实质是一个数组,所以在List的未必添加数据是比较快的,但是如果在数据的头或者中间添加删除数据相对来说更低效一些因为会影响其它数据的重新排列。

LinkedList<T>

LinkedList在内部维护了一个双向的链表,也就是说我们在LinkedList的任何位置添加或者删除数据其性能都是很快的。因为它不会导致其它元素的移动。一般情况下List已经够我们使用了,但是如果对这个集合在中间的添加删除操作非常频繁的话,就建议使用LinkedList。

HashSet<T>

HashSet是一个无序的能够保持唯一性的集合。我们也可以把HashSet看作是Dictionary<TKey,TValue>,只不过TKey和TValue都指向同一个对象。HashSet非常适合在我们需要保持集合内元素唯一性但又不需要按顺序排列的时候。

HashSet不支持下标访问。

SortedSet<T>

SortedSet和HashSet,就像SortedDictionary和Dictionary一样,还记得这两个的区别么?SortedSet内部也是一个二叉树,用来支持按顺序的排列元素。

Stack<T>

后进先出的队列 
不支持按下标访问

Queu<T>

先进先出的队列 
不支持按下标访问


推荐使用场景

这里写图片描述


非泛型类集合

泛型集合类是在.NET2.0的时候出来的,也就是说在1.0的时候是没有这么方便的东西的。现在基本上我们已经不使用这些集合类了,除非在做一些和老代码保持兼容的工作的时候。来看看1.0时代的.NET程序员们都有哪些集合类可以用。

ArraryList 
后来被List<T>替代。

HashTable 后来被Dictionary<TKey,TValue>替代。 
Queue 后来被Queue<T>替代。 
SortedList 后来被SortedList<T>替代。 
Stack 后来被Stack<T>替代。

线程安全的集合类

ConcurrentQueue 线程安全版本的Queue 
ConcurrentStack线程安全版本的Stack 
ConcurrentBag线程安全的对象集合 
ConcurrentDictionary线程安全的Dictionary 
BlockingCollection


.NET为我们提供的集合类是我们很常用的工具类之一,希望这篇文章能够帮助大家更好的认识这些集合类。当然,个人感觉还有不完善的地方,比如说HashTable和Binary Search Tree就没有细究下去,包括单向链表和双向链表之间的对比本文也没有提及。感兴趣的朋友可以深入了解一下。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/438321.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Spring AOP(通知、连接点、切点、切面)

一、AOP术语 通知&#xff08;Advice&#xff09;   切面的工作被称为通知。通知定义了切面是什么以及何时使用。除了描述切面要完成的工作&#xff0c;通知还解决了何时执行这个工作的问题。 5种通知类型&#xff1a;前置通知&#xff08;Before&#xff09;&#xff1a;在…

C#中几种常用的集合的用法

集合:将一推数据类型相同的数据放入到一个容器内&#xff0c;该容器就是数组&#xff1a;内存中开辟的一连串空间。 非泛型集合 ArrayList集合&#xff1a; ArrayList是基于数组实现的&#xff0c;是一个动态数组&#xff0c;其容量能自动 增长 ArrayList的命名空间System.…

C#使用Redis的基本操作

一&#xff0c;引入dll 1.ServiceStack.Common.dll 2.ServiceStack.Interfaces.dll 3.ServiceStack.Redis.dll 4.ServiceStack.Text.dll 二&#xff0c;修改配置文件 在你的配置文件中加入如下的代码&#xff1a; <appSettings><add key"RedisPath" value…

Navicat将mysql表结构导成oracle表结构

1&#xff0c;选中对应的表右键逆向表到模型 2.点击右上角文件转换模型为 3.模型选择物理&#xff0c;数据库oracle&#xff0c;选择对应的版本 4.新弹出的模型点击右上角文件&#xff0c;导出sql 5.选择路径导出sql

程序员们的三高:高并发、高性能、高可用

你们知道淘宝&#xff0c;京东这些购物商场吗&#xff1f;他们到了双11&#xff0c;双12为什么能支持全国14亿人口同时购物下单呢&#xff0c;因为他们的程序做到了高并发、高性能、高可用。那么你对程序员的三高了解多少呢&#xff1f; 高并发 一. 高并发 高并发是现在互联…

char 和 varchar 的区别,数据库索引B+树

char 和 varchar 的区别 char(n) &#xff1a;固定长度&#xff0c;效率高&#xff1b;缺点&#xff1a;占用空间&#xff1b;存储固定长度的&#xff0c;使用 char 非常合适。 varchar(n) &#xff1a;可变长度&#xff0c;存储的值是每个值占用的字节再加上一个用来记录其长…

C#基础操作符详解

本节内容&#xff1a; 1.操作符概览&#xff1b; 2.操作符的本质&#xff1b; 3.操作符与运算顺序 4.操作符详解。 1.操作符概览&#xff1a; 操作符&#xff08;Operator&#xff09;也译为”运算符” 操作符是用来操作数据的&#xff0c;被操作符操作的数据称为操作数&a…

C# 有什么惊艳到你的地方?

作者&#xff1a;皮皮关 链接&#xff1a;https://www.zhihu.com/question/335137780/answer/786853293 来源&#xff1a;知乎 著作权归作者所有。商业转载请联系作者获得授权&#xff0c;非商业转载请注明出处。 很多游戏开发者都是由于Unity而“被迫”使用C#的。但用过一段…

SqlServer学习之存储过程

前言&#xff1a;对于存储过程一直有一种抵触的心理&#xff0c;因为毕业至今所在的公司开发组都不是很规范&#xff0c;对于开发的一些注意事项并没有很多的规定&#xff0c;只是在知乎上查找相关知识的时候&#xff0c;看到很多人对于在程序里使用存储过程的不好之处都有很多…

中间件之RPC

一、RPC的定义 1、RPC(Romote Procedure Call)&#xff1a;远程过程调用&#xff0c;允许一台计算机程序远程调用另外一台计算机的子程序&#xff0c;不用关心底层网络通信 2、应用&#xff1a;分布式网络通信 3、在Socket的基础上实现&#xff0c;比socket需要更多资源 4、…

需求分析之UML用例图学习

用例图常用的三种关系浅析&#xff1a; &#xff08;一&#xff09;泛化(Inheritance) 通常理解的继承关系 &#xff08;二&#xff09;包含&#xff08;include&#xff09; 分解功能&#xff0c;一定包含的功能 &#xff08;三&#xff09;拓展(extend) 附加功能&#xff0c…

UML之用例图(use case)箭头方向

1、Association&#xff0c;无箭头&#xff0c;Actor连接UseCase即可&#xff1b; 2、DirectedAssocition&#xff0c;Actor连接UseCase&#xff0c;箭头由Actor指向UseCase&#xff08;角色指向用例&#xff09;&#xff1b; 3、Generalization&#xff0c;继承&#xff0c;…

UML-记忆技巧

箭头方向 UML箭头方向&#xff1a;从子类指向父类&#xff0c;读作继承自定义子类时需要通过extend关键字指定父类子类一定时知道父类定义的&#xff0c;但父类并不知道子类的定义 只有知道对方信息时才能指向对方 image.png 实现-继承|虚线-实现 空心三角箭头&#xff1…

国密算法SM2,SM3,SM4-java实现

SM2是国家密码管理局于2010年12月17日发布的椭圆曲线公钥密码算法&#xff0c;基于ECC。其签名速度与秘钥生成速度都快于RSA&#xff0c;非对称加密&#xff0c;该算法已公开 SM3是中华人民共和国政府采用的一种密码散列函数标准&#xff0c;由国家密码管理局于2010年12月17日…

数据库事务隔离级别-- 脏读、幻读、不可重复读(清晰解释)

一、数据库事务隔离级别 数据库事务的隔离级别有4个&#xff0c;由低到高依次为Read uncommitted 、Read committed 、Repeatable read 、Serializable &#xff0c;这四个级别可以逐个解决脏读 、不可重复读 、幻读 这几类问题。 √: 可能出现 : 不会出现 脏读不可重复读幻…

原子性和一致性的区别是什么?

注&#xff1a; 重度参考了&#xff1a;https://www.zhihu.com/question/30272728/answer/72476703&#xff08;讲的非常好&#xff0c;逻辑清晰&#xff09; 轻度参考了&#xff1a;https://www.cnblogs.com/fjdingsd/p/5273008.html&#xff08;概念也比较清晰&#xff0c;…

什么是事务、事务特性、事务隔离级别、spring事务传播特性

1.什么是事务&#xff1a; 事务是指程序中的一个操作序列。其特点是&#xff1a;该序列的所有操作要么全部成功完成&#xff0c;要么只要有一个操作失败&#xff0c;则该序列所有操作都将被撤销。这也是事务的原子性&#xff08;要么成功&#xff0c;要么失败&#xff09;。 …

软件工程 – 开发模型

软件工程 – 开发模型 为了指导软件开发&#xff0c;可以用不同的方式将软件生命周期中的所有开发活动组织组织起来从而形成不同的开发模型。 瀑布模式 瀑布模型严格遵守软件生命周期各阶段的固定顺序:计划、分析、设计、编程、测试和维护&#xff0c;上一阶段完成才能进入到…

排序算法之选择排序

动图演示 使用场景:适用于大多数排序,数据量大效率明显优于冒泡 java代码实现 import java.util.Arrays;/*** 选择排序*/ public class SelectSort {private static int[] selectSort(int[] arr) {if (arrnull||arr.length<2){return arr;}//控制循环次数,最后一个元素不用…

VS的包含目录、库目录、引用目录、可执行目录解释

来源&#xff1a;包含目录、库目录、附加包含目录、附加库目录、附加依赖项之详解 学习备份 VS项目中的包含目录、库目录、附加包含目录、附加库目录、附加依赖项均在”项目->属性->配置属性”下进行配置&#xff0c;具体说明如下&#xff1a; VC目录&#xff1a; 包含目…