您的位置:首页 - 教程 - C# - 正文
C# System.Collections.Generic下的泛型集合

C#中的泛型集合全部归结在System.Collections.Generic命名空间下。说起泛型集合,相信大家用到的最多的就是List<T>和 Dictionary<T1, T2> 这两个吧,反正本人用的最多的就是这两个,只要一想到泛型集合,就是二选一,也从来没有考虑过是不是适合。后来仔细想了一下感觉哪里不对,如果这两个可以满足任何需求的,.net里就没有必要弄那么一大堆集合类了。虽然上面两种集合可以满足平时的需求,但是感觉以后还是要根据场合挑选一下再用,这毕竟也是个好的习惯,至少也要了解一下这么多集合都是干什么的。

       让我们先来看一下命名空间下大概都有哪些集合类,在MSDN中我们可以看到大概有一下几个类表示泛型集合:

除却最后三个关于线程安全的不讲,剩下的有:

Dictionary<TKey, TValue> 、HashSet<T> 、LinkedList<T> 、List<T> 、 Queue<T> 、 SortedDictionary<TKey, TValue> 、 SortedList<TKey, TValue> 、 SortedSet<T> 、 Stack<T>  共九个。

我们一个个简单说一下。

 

1、Dictionary<TKey,TValue>

        这是一个键值集合,是键值集合中使用最多的一个类。这个集合最大的优点就是通过键值插入、删除和查询操作,是在这几个集合中最快的一个(接近于O(1)),这全都得益于它是作为一个哈希表来实现的;其缺点就是由于是通过哈希的形式存储,所以表中的数据是无序的,你并不能通过一定的顺序遍历表中的数据。

       注意事项:(1)如果一个类型被用作键值的类型,那么这个类型必须实现GetHashCode() 和 Equals()  方法,因为要被哈希,并且该类型提供的哈希算法的质量决定了通过键值操作集合的运算复杂度, 你最好还好实现IEqualityComparer  接口用做键值比较,否则将会使用默认比较器。(2)键的值必须唯一,且不能为null,除非Tvalue为引用类型。

       总结:当你维护的一个集合不要求排序,而主要用来插入、删除和查询的话,推荐使用此集合。

 

2、SortedDictionary<TKey,TValue>

        这个集合在使用上和Dictionary<TKey,TValue>很相似,但是在实现上大不相同。这个集合是遍历一个运算复杂度为O(log n)的二叉搜索树,而不是哈希表,其中n是元素的个数。这个集合会在列表变更的时候做一次排序操作。与SortedList<TKey,TValue>相比,优点是可对未排序的数据执行更快的插入和删除操作(O(log n));缺点是使用的内存比SortedList<TKey,TValue>多,并且如果使用排序数据一次性填充列表,则 SortedList<TKey, TValue> 比 SortedDictionary<TKey, TValue> 快。

        注意事项:(1)如果一个类型被用作键值的类型,则此类型要实现IComparer<T> ,因为要比较键值进行排序 ,都则会用默认的比较器。(2)键的值必须唯一,且不能为null,除非TValue为引用类型。

        总结:当你要维护一个集合按一定规则排序时(初始如果是未排序的),并且会频繁用来插入、删除或查询的话,推荐使用此集合。

 

3、SortedList<TKey,TValue>

        和SortedDictionay<TKey,TValue>想是,也是一个根据键值进行排序的键值对,但是这个遍历的是一个复杂度为O(log n)的数组,n是元素的个数。与SortedDictionary<TKey,TValue>相比,其优点是占用内存较少,使用排序数据一次性填充列表比SortedDictionary<TKey,TValue>快,并且SortedList<TKey, TValue> 支持通过由 Keys 和 Values 属性返回的集合对键和值执行高效的索引检索。 访问此属性时无需重新生成列表,因为列表只是键和值的内部数组的包装。;缺点是对于插入和删除操作,会相对慢一些(O(n)),因为它遍历的是一个数组,但是对于查询,复杂度和SortedDictionary<TKey,TValue>是一样的。

        注意事项:(1)如果一个类型被用作键值的类型,则此类型要实现IComparer<T> ,因为要比较键值进行排序 ,都则会用默认的比较器。(2)键的值必须唯一,且不能为null,除非TValue为引用类型。

        总结:当你要维护一个集合按一定规则排序时(初始如果是排序的),并且只会频繁的查询,而很少插入和删除的话,推荐使用此集合。

 

4、List<T>

       大小按需动态增加的数组,是ArrayList的泛型实现。List<T> 类即使用相等比较器又使用排序比较器。诸如 Contains、IndexOf、LastIndexOf 和 Remove 这样的方法对列表元素使用相等比较器。 类型 T 的默认相等比较器按如下方式确定。 如果类型 T 实现 IEquatable<T> 泛型接口,则相等比较器为该接口的 Equals(T) 方法;否则,默认相等比较器为 Object.Equals(Object)。 诸如 BinarySearch 和 Sort 这样的方法对列表元素使用排序比较器。 类型 T 的默认比较器按如下方式确定。如果类型 T 实现 IComparable<T> 泛型接口,则默认比较器为该接口的 CompareTo(T) 方法;否则,如果类型 T 实现非泛型 IComparable 接口,则默认比较器为该接口的 CompareTo(Object) 方法。 如果类型 T 没有实现其中任一个接口,则不存在默认比较器,并且必须显式提供比较器或比较委托。 List<T> 不保证是排序的。在执行要求 List<T> 已排序的操作(例如 BinarySearch)之前,您必须对 List<T> 进行排序。需要注意的是,因为这个是一个数组,所以其对插入和删除这类的操作效率并不高,但是对查询的效率是很快的,所以使用这个最好不要频繁插入删除数据。

 

5、HashSet<T>

       这是一组值的集合,提供高性能的集运算,里面是一组不重复出现且无特定储存顺序的元素。 它主要的优势就是运算快,所以对于没有排序要求的情况下,推荐使用这个。

 

6、LinkedList<T>

       这个是双向链表(貌似没有单链表)。插入和移除的运算复杂度为 O(1)。可以移除节点,然后在同一列表或其他列表中重新插入它们,这样在堆中便不会分配额外的对象。 由于该列表还维护内部计数,因此获取 Count 属性的运算复杂度为 O(1)。 LinkedList<T> 接受 null 作为引用类型的有效 Value 属性,并且允许重复值。 如果 LinkedList<T> 为空,则 First 和 Last 属性为 null。

 

7、Queue<T>

      表示先进先出的队列集合。Queue<T> 的容量是 Queue<T> 可以包含的元素数。 当向 Queue<T> 中添加元素时,将通过重新分配内部数组来根据需要自动增大容量。Queue<T> 接受 null 作为引用类型的有效值并且允许有重复的元素。一般用于消息处理。

 

8、SortedSet<T>

       这是一个排序的集合对象,当对象插入和删除的时候,排序操作是不影响其性能的。其中的元素不可重复。但要注意,这个是在4.0版本中才加入的。

 

9、Stack<T>

       表示先进后出的集合,即栈。Stack<T> 作为数组来实现。Stack<T> 的容量是 Stack<T> 可以包含的元素数。 当向 Stack<T> 中添加元素时,将通过重新分配内部数组来根据需要自动增大容量。如果 Count 小于堆栈的容量,则 Push 的运算复杂度是 O(1)。 如果需要增加容量以容纳新元素,则 Push 的运算复杂度成为 O(n),其中 n 为 Count。 Pop 的运算复杂度为 O(1)。 Stack<T> 接受 null 作为引用类型的有效值并且允许有重复的元素。


评论: