快速入门系列--clr--03泛型集合

张玉宝 张玉宝     2022-09-04     171

关键词:

.NET中的泛型集合

在这里主要介绍常见的泛型集合,很多时候其并发时的线程安全性常常令我们担忧。因而简述下.NET并发时线程安全特性,其详情请见MSDN。

  • 普通集合都不支持多重并发写操作
  • 部分支持单线程写和并发读操作
  • 同时.NET4添加了大量并发集合

 

首先介绍常见的泛型集合接口,其大部分都位于System.Collection.Generic命名空间。

  • IEnumerable<T>,其可以获取一个IEnumerator<T>迭代器,如果从数据库的角度来看,前者是表,后者是游标,同时这两个接口是唯一具有可变性的集合接口。
  • ICollection<T>,它扩展了IEnumerable<T>,添加了Count和IsReadOnly属性,Add和Remove等操作方法,Contains等判定函数,所有的标准泛型集合都实现了该接口。
  • IList<T>,提供定位功能,包括一个索引器、Insert和RemoveAt,我们通常认为可以通过索引对该泛型集合进行随机访问。、
  • IDictionary<TKey, TValue>,表示键值对集合,扩展了ICollection<KeyValuePair<TKey, TValue>>,取值可以用TryXXX方式。
  • ISet<T>表示唯一值集,包含大量集合操作:交、并、补。

 

接下来介绍具体的集合泛型集合类型,在实际中需要根据具体场景选择最适合的集合类型。

  • List<T>,其是列表的默认选择,内含一个数组,并且提供列表的逻辑大小Count和后台数组的大小Capacity,当数组满了时,会进行扩容。由于是连续型的数据结构,其添加删除操作的成本较高,提供二分查找,查找效率高。同时,其Sort操作会修改原始列表的内容,与OrderBy不同,并且Sort是不稳定的,会出现相等元素顺序不同的情况。
  • 数组,最基础的集合,均派生自System.Array,包括一维数组T[10],二维数组T[10, 20]等,通过Array类的静态方法进行ConvertAll、FindAll和BinarySearch等操作。
  • Colletion<T>,位于System.Colletion.ObjectModel命名空间,为BindingList<T>和ObservableCollection<T>等扩展类型提供基类。与双向绑定相关的集合类型,注意它们只会在包装器发生变化发出通知,而基础列表改变时不会引发任何事件。
  • ReadOnlyCollection<T>和ReadOnlyObservableCollection<T>,其也类似于包装器,后者实现了INotifyCollectionChanged, INotifyPropertyChanged两个接口。
  • Dictionary<TKey, TValue>,使用散列表,查找性能的优劣取决于散列函数的优劣,默认使用Equals和GetHashCode,可以通过制定IEqualityComparer<TKey>作为参数。
  • SortList<TKey, TValue>和SortedDictionary<TKey, TValue>,两者都是字典类,前者内部维护一个排序的数组,添加删除操作的事件复杂度为O(n),后者内部维护一个红黑树,添加删除操作事件复杂度为O(log n),但会消耗更多的堆内存,使用IComparer<TKey>作比较。
  • HashSet<T>,是不含值的Dictionary<,>,具有相同性能特性,并且所维护顺序一般与添加顺序无关。
  • SortedSet<T>,是没有值得SortedDictionary<,>,维护一个红黑树,添加删除和检查操作的事件复杂度为O(log n)。提供GetViewBetween方法返回介于原始集上下限之间的另一个SortedSet<T>,注意这是一个动态的视图,会随着原始集的改变而改变。尽管看起来很方便,但需要注意的是"天下没有免费的午餐",为保持内部一致性,操作的代价更大。
  • Queue<T>,构建一个环形缓冲区,实际维护一个基础数组,包含两个索引,分别记住入队和出队的位置(Slot),如果入队指针追上出队指针,则进行扩容。提供Enqueue、Dequeue、Peek等方法进行入队、出队、查看操作。
  • Stack<T>,其实现更简单,可以看做是一个提供Push、Pop、Peek操作的List<T>。

 

最后介绍并行集合,也就是线程安全的集合。(注意所有的并发类型都未实现IList<T>接口)

  • IProducerConsumerCollection<T>和BlockingCollection<T>,前者是生产者/消费者模型中数据存储的抽象,后者是其包装类,使用ConcurrentQueue<T>作为后台存储,提供ToArray方法获得集合当前状态快照,TryXXX方法允许有效的失败模式减少对锁的需求。(例如,当队列中只有一个项时,两个线程同时判断它是否有项,并且都返回true,这是一个线程执行了出队操作,而另外一个线程在执行出队操作时,将抛出异常,因而需要对验证队列是否有项操作和有项就出队操作作为一个整体,需要添加锁)
  • ConcurrentBag<T>,ConcurrentQueue<T>,ConcurrentStack<T>,它们是对IProducerConsumerCollection<T>的实现,其GetEnumerator()方法返回集合快照,迭代时可以改变集合,但该改变不会反应到迭代器中。
  • ConcurrentDictionary<TKey, TValue>, 实现了IDictionary<TKey, TValue>接口。支持并发的读写和线程安全的迭代,但不同是,其在迭代过程中对字典的改变不能确定是否反应到迭代器上。

 

小节:在日常工作中,当遇到需要并发操作非集合类型的全局变量时,需要使用锁来处理;而当是集合类型时,就需要使用对应的并行集合类来处理,其能很好的TPL协作在一起。尤其在使用非线程安全的字典类进行并发操作时,有时会出现死循环等情形,尤其需要注意。

Tip:where T:new() 

 

参考文献

  1. Jon, Skeet. 深入理解C#(第3版)[M]. 北京:人民邮电出版社, 2014. 469-483

scala入门系列(十三):类型参数

引言Scala中类型参数是什么呢?其实就类似于Java中的泛型。定义一种类型参数,比如在集合、类、函数中定义类型参数,然后就可以保证使用到该类型参数的地方就只能是这种类型,从而实现程序更好的健壮性。 泛型类泛... 查看详情

(java多线程系列一)快速入门

Java多线程快速入门1、线程和进程的区别进程是所有线程的集合,每一个线程是进程的一条执行路径。2、多线程的应用场景多线程主要体现在提高程序的效率,比如迅雷多线程下载,多线程分批发送短信等。3、多线程的创建方... 查看详情

泛型-泛型入门

一泛型的由来    Java集合有个缺点,把一个对象"丢进"集合里之后,集合就会"忘记"这个对象的数据类型,当再次取出该对象时候,该对象的编译类型就变成了Object类型(其运行时类型没变)    Java集合之所以这样是因为设计者... 查看详情

java集合入门和深入学习,看这篇就差不多了

一、集合入门总结集合框架:Java中的集合框架大类可分为Collection和Map;两者的区别:1、Collection是单列集合;Map是双列集合2、Collection中只有Set系列要求元素唯一;Map中键需要唯一,值可以重复3、Collection的数据结构是针对元素... 查看详情

java入门系列泛型

前言     Java7泛型的“菱形”语法在JavaSE7之前,声明泛型对象的代码如下:List<String>list=newArrayList<String>();而在Java7中,可以使用如下代码:List<String>list=newArrayList<>();&nb 查看详情

mobx知识点集合案例(快速入门)(代码片段)

文章目录一、文章参考二、知识点串联2.1使用注解方式串联2.2使用函数一、文章参考bilibiliReact系列教程之Mobxmobx中文文档二、知识点串联2.1使用注解方式串联importReact,Componentfrom'react';importReactDOMfrom'react-dom';import'./i... 查看详情

mobx知识点集合案例(快速入门)(代码片段)

文章目录一、文章参考二、知识点串联2.1使用注解方式串联2.2使用函数一、文章参考bilibiliReact系列教程之Mobxmobx中文文档二、知识点串联2.1使用注解方式串联importReact,Componentfrom'react';importReactDOMfrom'react-dom';import'./i... 查看详情

mobx知识点集合案例(快速入门)(代码片段)

文章目录一、文章参考二、知识点串联2.1使用注解方式串联2.2使用函数一、文章参考bilibiliReact系列教程之Mobxmobx中文文档二、知识点串联2.1使用注解方式串联importReact,Componentfrom'react';importReactDOMfrom'react-dom';import'./i... 查看详情

linq学习系列-----3.1查询非泛型集合

 一.问题起源   LINQtoobject在设计时,是配合IEnumerable<T>接口的泛型集合类型使用的,例如字典、数组、List<T>等,但是对于继承了IEnumerable的非泛型集合如何处理,例如ArrayList。二.解决办法   上... 查看详情

全栈开发系列

一、全栈开发之HTML全栈开发之HTML快速入门(一)二、全栈开发之CSSCSS快速入门-引入方式CSS快速入门-基本选择器CSS快速入门-组合选择器CSS快速入门-属性和伪类CSS快速入门-盒子模型CSS快速入门-实用技巧CSS快速入门-float和positionCS... 查看详情

java基础系列--08_集合1

---恢复内容开始---集合当中有很多都是应用到泛型的技术,所以在讲集合之前,应该先将泛型的概念普及一下。泛型:  (1)泛型是一种类型,但是这种类型是在编译或者调用方法时才确定。  (2)格式:   ... 查看详情

《c#零基础入门之百识百例》(八十一)泛型概念介绍--泛型类/结构/接口/委托

C#零基础入门泛型和系统类--泛型概念介绍--泛型类/结构/接口/委托前言一,泛型概念二,泛型类2.1声明泛型类2.2使用泛型类一,泛型结构三,泛型接口一,泛型接口声明二,泛型接口使用四,泛型委托4.1泛型委托声明4.2泛型委托... 查看详情

backbone入门系列集合

...口相声,也可以对口,也可以群口,,,在前文,也就是入门系列3的基础上,添加js代码varnoteCollection=Backbone.Collection.extend({ model:Note//指定相关模型});varnote1=newNote({id:1,title:"西红柿"});//设置id,表明唯一性v 查看详情

《c#零基础入门之百识百例》(八十二)泛型类型参数where约束--泛型单例

C#零基础入门泛型和系统类泛型类型参数Where约束--泛型单例前言一,类型参数约束1.1Where子句1.2约束类型和次序二,泛型方法2.1声明泛型方法2.2调用泛型方法三,实现泛型单例工具类3.1实现代码3.2使用代码前言本文属于C#零基础... 查看详情

gradle学习系列之一——gradle快速入门

Gradle学习系列之一——Gradle快速入门这是一个关于Gradle的学习系列,其中包含以下文章:Gradle快速入门创建Task的多种方法读懂Gradle语法增量式构建自定义Property使用javaPlugin依赖管理构建多个Project自定义Task类型自定义Plugin  ... 查看详情

python数据科学快速入门系列|06matplotlib数据可视化基础入门(代码片段)

.../robotsfutures.blog.csdn.net/article/details/126899226《Python数据科学快速入门系列》快速导航:【Python数据科学快速入门系列|01】Numpy初窥——基础概念【Python数据科学快速入门系列|02】创建ndarray对象的十多种方法【Python数据科 查看详情

《c#零基础入门之百识百例》(八十二)泛型类型参数where约束--泛型单例

C#零基础入门泛型和系统类泛型类型参数Where约束--泛型单例前言一,类型参数约束1.1Where子句1.2约束类型和次序二,泛型方法2.1声明泛型方法2.2调用泛型方法三,实现泛型单例工具类3.1实现代码3.2使用代码前言本文属于C#零基础... 查看详情

elasticsearch顶尖高手系列-快速入门篇

...索引、分片、副本等05.在windows上安装和启动Elasticseach06.快速入门案例实战之电商网站商品管理:集群健康检查,文档CRUD07.快速入门案例实战之电商网站商品管理:多种搜索方式08.快速入门 查看详情