本片文章引用panpan_chen大神的文章来对自己提升对C#底层的理解使用
之前在做Unity项目的时候List、Dictionary、ArrayList、Hashtable等集合用的还是非常多的但是一直没有研究过他的底层到底是如何实现的,今天看到了上面提到的这篇文章,颇有收益,非常感谢原创作者。
这里有不太明白FCL的可以去我之前总结的一片文章
.NET中的 FCL(Framework Class Library)我们平时使用的各种类库都包含在这里,它其中还包含BCL基础类库,如IO,String,Net等。
这里我们把集合单独拎出来说一下,所有的集合都是继承自IEnumerable。集合总体可以分为以下几类:
关联/非关联型集合,顺序/随机访问集合,顺序/无序集合,泛型/非泛型集合,线程集合。
1.泛型集合是类型安全的、基于固定的泛型T,运行时不需要像非泛型的执行Object和具体类型的转换问题。
2.泛型集合的效率相对较高。
3.两者都能实现数据存储,不同的是泛型只能存放T类型数据,有运行时检测,而非泛型的都转化为Object存储,能存储任意类型,包括值类型,会带来装箱拆箱的性能损耗,同时都是Object类型(弱类型)编译时无法类型检测,运行时会导致类型不一致的安全性问题。
都是抽象类,不能实例化;
目的是提供给用户自定义实现强类型的集合,解决一般非泛型集合的弱类型不安全的问题。
IEnumerator定义了我们遍历集合的基本方法,以便我们可以实现单向向前的访问集合中的每一个元素。 所有的集合类都继承了IEnumerator接口,包括String类。而IEnumerable只有一个方法GetEnumerator即得到遍历器。这里泛型与非泛型的方法都是一样的。
从最上图我们可以知道,ICollection是直接继承自IEnumerable。而实际上也是如此,我们可以说ICollection比IEnumerable多支持一些功能,不仅仅只提供基本的遍历功能,还包括:
统计集合和元素个数
是否同步
同步访问数组对象
拷贝到某个位置
ICollection 与ICollection 略有不同,如上图,ICollection不提供编辑集合的功能,即Add和Remove。包括检查元素是否存在Contains也不支持。
IList则是直接继承自ICollection和IEnumerable。所以它包括两者的功能,并且支持根据下标访问和添加元素。IndexOf, Insert, RemoveAt等等。我们可以这样说,IEnumerable支持的功能最少,只有遍历。而ICollection支持的功能稍微多一点,不仅有遍历还有维护这个集合的功能。而IList是最全的版本。相当于初版、升级版、最终版一直迭代到现在。
IList与IList 略有不同,如上图,IList提供编辑集合的功能,即Add、Remove、Contains等功能,而IList是没有具备这几个功能。因为ICollection与ICollection 存在差异,所以说这里注定IList与IList是存在不同。
这个是在Framework4.5中新增的接口类型,可以被看作是IList的缩减版,去掉了所有可能更改这个集合的功能。比如:Add, RemoveAt等等。
因为我这里用的是Framework v4.0.30319所以还没有这个功能,但是这个功能的作用肯定是增加安全性,放置外部随意去修改集合内部数据这一点毋庸置疑。
IDictionary提供了对键值对集合的访问,也是继承了ICollection和IEnumerable,扩展了通过Key来访问和操作数据的方法。
这里注意一点,所有非泛型都是实现了ICloneable的接口,然而这个接口的作用就是赋值功能。
关联性集合类即我们常说的键值对集合,允许我们通过Key来访问和维护集合。我们先来看一下 FCL为我们提供了哪些泛型的关联性集合类:
Dictionary
SortedDictionary
SortedList
Dictionary
是我们最常用的关联性集合了,它的访问,添加,删除数据所花费的时间是所有集合类里面最快的,因为它内部用了Hashtable作为存储结构,所以不管存储了多少键值对,查询/添加/删除所花费的时间都是一样的,它的时间复杂度是O(1)。
Dictionary
作为TKey的类型必须实现GetHashCode()和Equals() 或者提供一个IEqualityComparer,否则操作可能会出现问题。
SortedDictionary
SortedList
非关联性集合就是不用key操作的一些集合类,通常我们可以用元素本身或者下标来操作。FCL主要为我们提供了以下几种非关联性的泛型集合类。
List
LinkedList
HashSet
SortedSet
Stack
Queue
泛型的List 类提供了不限制长度的集合类型,List在内部维护了一定长度的数组(默认初始长度是4),当我们插入元素的长度超过4或者初始长度 的时候,会去重新创建一个新的数组,这个新数组的长度是初始长度的2倍(不永远是2倍,当发现不断的要扩充的时候,倍数会变大),然后把原来的数组拷贝过来。所以如果知道我们将要用这个集合装多少个元素的话,可以在创建的时候指定初始值,这样就避免了重复的创建新数组和拷贝值。
另外的话由于内部实质是一个数组,所以在List的未必添加数据是比较快的,但是如果在数据的头或者中间添加删除数据相对来说更低效一些因为会影响其它数据的重新排列。
LinkedList在内部维护了一个双向的链表,也就是说我们在LinkedList的任何位置添加或者删除数据其性能都是很快的。因为它不会导致其它元素的移动。一般情况下List已经够我们使用了,但是如果对这个集合在中间的添加删除操作非常频繁的话,就建议使用LinkedList。
HashSet是一个无序的能够保持唯一性的集合。我们也可以把HashSet看作是Dictionary
HashSet不支持下标访问。
SortedSet和HashSet,就像SortedDictionary和Dictionary一样,还记得这两个的区别么?SortedSet内部也是一个二叉树,用来支持按顺序的排列元素。
后进先出的队列
不支持按下标访问
先进先出的队列
不支持按下标访问
泛型集合类是在.NET2.0的时候出来的,也就是说在1.0的时候是没有这么方便的东西的。现在基本上我们已经不使用这些集合类了,除非在做一些和老代码保持兼容的工作的时候。来看看1.0时代的.NET程序员们都有哪些集合类可以用。
后来被List替代。
HashTable 后来被Dictionary
Queue 后来被Queue替代。
SortedList 后来被SortedList替代。
Stack 后来被Stack替代。
ConcurrentQueue 线程安全版本的Queue
ConcurrentStack线程安全版本的Stack
ConcurrentBag线程安全的对象集合
ConcurrentDictionary线程安全的Dictionary
BlockingCollection