浅谈C#集合【转】
集合是.NET FCL(Framework Class Library)中很重要的一部分,也是我们开发当中最常用到的功能之一,几乎是无处不在。俗话说知其然,知其所以然,平常看到IEnumerable,IEnumerator,ICollection是不是知道他们之间各自的区别?除了List和Dictionary以外,你还用过哪些其它的集合类?废话少说,今天我们就来看一些这些定义集合类的接口以及他们的实现。
- 集合接口
- 关联性泛型集合类
- 非关联性泛型集合类
- 推荐使用场景
- 非泛型集合类
- 线程安全集合类
集合接口
先来看一下,FCL为我们提供了哪些接口:
IEnumerable 和IEnumberator
1 2 3 4 5 6 7 |
public interface IEnumerator { bool MoveNext(); object Current { get; } void Reset(); } |
IEnumerator定义了我们遍历集合的基本方法,以便我们可以实现单向向前的访问集合中的每一个元素。而IEnumerable只有一个方法GetEnumerator即得到遍历器。
1 2 3 4 |
public interface IEnumerable { IEnumerator GetEnumerator(); } |
注意:我们经常用的foreach即是一种语法糖,实际上还是调用Enumerator里面的Current和MoveNext实现的遍历功能。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
List<string> list = new List<string>() { "Jesse", "Chloe", "Lei", "Jim", "XiaoJun" }; // Iterate the list by using foreach foreach (var buddy in list) { Console.WriteLine(buddy); } // Iterate the list by using enumerator List<string>.Enumerator enumerator = list.GetEnumerator(); while (enumerator.MoveNext()) { Console.WriteLine(enumerator.Current); } |
上面的代码中用到的foreach和enumerator到IL中最后都会被翻译成enumerator的MoveNext和Current。
IEnumerable是一个很有用的接口,实现它的好处包括:
- 支持foreach语句
- 作为一个标准的集合类与其它类库进行交互
- 满足更复杂的集合接口的需求
- 支持集合初始化器
当然实现的方法也有很多,如下:
- 如果我们集合是通过封装其它集合类而来的,那么我们可以直接返回这个集合的enumerator
- 通过yield return 来返回
- 实现我们自己的IEnumerator来实现
这里给大家演示一下如何通过yield来实现返回enumerator
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
public class BuddyList : IEnumerable { private string[] data= new string[] { "Jesse", "Chloe", "Lei", "Jim", "XiaoJun" }; public IEnumerator GetEnumerator() { foreach (var str in data) { yield return str; } } } var myBuddies= new BuddyList(); foreach (var str in myBuddies) { Console.WriteLine(str); } |
ICollection<T>和ICollection
从最上面第一张图我们可以知道,ICollection是直接继承自IEnumerable。而实际上也是如此,我们可以说ICollection比IEnumerable多支持一些功能,不仅仅只提供基本的遍历功能,还包括:
- 统计集合和元素个数
- 获取元素的下标
- 判断是否存在
- 添加元素到未尾
- 移除元素等等。。。
ICollection 与ICollection<T> 略有不同,ICollection不提供编辑集合的功能,即Add和Remove。包括检查元素是否存在Contains也不支持。
IList<T>和IList
IList则是直接继承自ICollection和IEnumerable。所以它包括两者的功能,并且支持根据下标访问和添加元素。IndexOf, Insert, RemoveAt等等。我们可以这样说,IEnumerable支持的功能最少,只有遍历。而ICollection支持的功能稍微多一点,不仅有遍历还有维护这个集合的功能。而IList是最全的版本。
IReadOnlyList<T>
这个是在Framework4.5中新增的接口类型,可以被看作是IList<T>的缩减版,去掉了所有可能更改这个集合的功能。比如: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://blog.jobbole.com/85554/】
答案:能用foreach遍历访问的对象必须是集合或数组对象,而这些都是靠实现超级接口IEnumerator或被声明 GetEnumerator 方法的类型
注:不一定要实现IEnumerable接口,但一定要实现GetEnumrator方法。
问:
但是我在平常使用foreach in 的时候都是直接用的呀,也没出什么问题呀。请问,这是为什么呢。 如果按题目中问的那样去实现超级接口IEnumerator或被声明 GetEnumerator 方法的类型,应该怎样去做呢? 谢谢
答:平常使用foreach in 的时候都是用的系统定义好的集合类(实现ICollection接口)。比如 arrayList,stringDictionary ,数组等。这些是平台封装好的,已近实现了IEnumerator 接口了。要让自己写的类也能时候foreach 就必须 在编写类的时候实现该接口。这样就可以事后foreach遍历了。比如 定义一个 people 的类,实现了IEnumerator接口。有一个数组 People[] tempPeople ,那么 就可以用: foreach( p in
tempPeople){ console.writeline(p.name);}这样就免去了用for 循环的麻烦。至于怎么样去实现Iemumerator接口。查一下MSDN 就可以啦。
浅谈C#集合【转】的更多相关文章
- 浅谈JAVA集合框架
浅谈JAVA集合框架 Java提供了数种持有对象的方式,包括语言内置的Array,还有就是utilities中提供的容器类(container classes),又称群集类(collection cl ...
- 浅谈 Java集合
Java 集合 集合是对象的容器,定义了多个对象进行操作的常用方法,可实现数组的功能. Java集合类库所处位置:java.util.*. 与现代的数据结构类库的常见做法一样,Java集合类库也将接口 ...
- 浅谈Collection集合
俗话说:一个东西,一件事都离不开三句话:"是什么,为什么,怎么办" 集合是什么: 集合简单的说一个数组集合的高级体现,用来存储数据或对象的容器: 集合为什么存在: 集合只是体现了对 ...
- 浅谈Swift集合类型
Swift 的集合表现形式由数组和字典组成.它可以完美的存储任何呢想存储的东西. 数组是一个同类型的序列化列表集合,它用来存储相同类型的不同值.字典也是一个数组,但它的存值方式类似于Map,通过一对一 ...
- 浅谈JAVA集合框架(转载)_常用的Vector和HashMap
原作者滴着:http://www.cnblogs.com/eflylab/archive/2007/01/20/625237.html Java提供了数种持有对象的方式,包括语言内置的Array,还有 ...
- 浅谈Java集合体系及底层实现原理
集合加载因子 https://blog.csdn.net/qq_34627002/article/details/79769261 底层原理: https://blog.csdn.net/qq_258 ...
- 浅谈Java的集合框架
浅谈Java的集合框架 一. 初识集合 重所周知,Java有四大集合框架群,Set.List.Queue和Map.四种集合的关注点不同,Set 关注事物的唯一性,List 关注事物的索引列表,Q ...
- 铁乐学Python_day07_集合and浅谈深浅copy
1.[List补充] 在循环一个列表时,最好不要使用元素和索引进行删除操作,一旦删除,索引会随之改变,容易出错. 如果想不出错,可以采用倒着删除的方法,因为倒着删除进行的话,只是后面元素的位置发生了变 ...
- C#集合类型——Array、ArrayList、List 之浅谈
在学习或工作中,集合是经常用到的,可以换一句话说“无项目无集合”,“项目皆有集合”.它一般存储一系列数据或者将一系列数据进行相关操作.在这里先大略谈一些集合类型的相关知识用于回顾. 数组(Array) ...
随机推荐
- C#中Console.WriteLine()函数输出格式详解
格式项都采用如下形式: {index[,alignment][:formatString]} 其中"index"指索引占位符,这个肯定都知道: ",alignment&q ...
- Mysql mysql lost connection to server during query 问题解决方法
在linux上新安装的mysql数据库远程连接速度很慢,用Navicate可以正常连接但是时间很长:使用toad连接提示Mysql mysql lost connection to server du ...
- Memcached源码分析之assoc.c
#include "memcached.h" #include <sys/stat.h> #include <sys/socket.h> #include ...
- 多线程的并发问题,lock用法
开启多个线程,每个线程中多次操作公共变量 using System; using System.Collections.Generic; using System.Linq; using System ...
- iOS开发中视图控制器ViewControllers之间的数据传递
iOS开发中视图控制器ViewControllers之间的数据传递 这里我们用一个demo来说明ios是如何在视图控制器之间传递重要的参数的.本文先从手写UI来讨论,在下一篇文章中讨论在storybo ...
- 数据可视化-使用EXCEL和PS制作一个复杂饼图
一个朋友让我帮忙用饼图去展现一下一堆数据,我说行,后来他给我后,我看到是一堆貌似从什么仪器中导出的一堆使用分隔符分开的文本文档,使用excel打开后如下(为了保护隐私忽略了用字母和数字代替原来的内容: ...
- leetcode-位运算
位运算可以大大减小算法空间复杂度,提高效率,很巧妙! 先说一下位运算的简单用法 1. 按位与 & 用途:清零,取一个数中的某些指定位,保留一个数中的某些指定位 2. 按位或 | 用途:将一个数 ...
- SQL SERVER 2000 数据恢复(分离数据库+附加数据库)
一.分离数据库 SQL Server 2000允许分离数据库的数据和事务日志文件,然后将其重新附加到同一台或另一台服务器上.分离数据库将从SQL Server 删除数据库,所以当点击“分离数据 ...
- python with用法
python中with可以明显改进代码友好度,比如: with open('a.txt') as f: print f.readlines() 为了我们自己的类也可以使用with, 只要给这个类增加两 ...
- 创建 Web 前端开发环境(node和npm以及git)
Web 前端开发涉及多种工具,这里将常用工具的安装和配置进行说明,提供了详细的说明,为后继的开发创建一个坚实的基础. 本文介绍的工具有:NodeJS, NPM, Bower, Git 和 Grunt. ...