C#笔记7:集合类
时间:2010-09-08 来源:luminji
C#笔记7:集合类
本章概要:
1:为什么要使用泛型集合
2:集合的线程安全
3:选择集合类
1:为什么要使用泛型集合
针对 .NET Framework 的 2.0 版和更高版本的应用程序应当使用 System.Collections.Generic 命名空间中的泛型集合类,与对应的非泛型类相比,这些类提供了更高的类型安全性和效率。为什么这么说呢,看下面的例子:
// The .NET Framework 1.1 way to create a list: System.Collections.ArrayList list1 = new System.Collections.ArrayList(); list1.Add(3); list1.Add(105); System.Collections.ArrayList list2 = new System.Collections.ArrayList(); list2.Add("It is raining in Redmond."); list2.Add("It is snowing in the mountains.");
添加到 ArrayList 中的任何引用或值类型都将隐式地向上强制转换为 Object。 如果项是值类型,则必须在将其添加到列表中时进行装箱操作,在检索时进行取消装箱操作。 强制转换以及装箱和取消装箱操作都会降低性能;在必须对大型集合进行循环访问的情况下,装箱和取消装箱的影响非常明显。
另一个限制是缺少编译时类型检查;因为 ArrayList 会将所有项都强制转换为 Object,所以在编译时无法防止客户端代码执行类似如下的操作:
System.Collections.ArrayList list = new System.Collections.ArrayList(); // Add an integer to the list. list.Add(3); // Add a string to the list. This will compile, but may cause an error later. list.Add("It is raining in Redmond."); int t = 0; // This causes an InvalidCastException to be returned. foreach (int x in list) { t += x; }
ArrayList 和其他相似类真正需要的是:客户端代码基于每个实例指定这些类要使用的具体数据类型的方式。 这样将不再需要向上强制转换为 T:System.Object,同时,也使得编译器可以进行类型检查。 换句话说,ArrayList 需要一个类型参数。 这正是泛型所能提供的。
2:集合的线程安全
.NET Framework 1.0 中的集合,如常用的 ArrayList 和 Hashtable通过 Synchronized 属性(此属性返回与集合有关的线程安全包装)提供某种线程安全性。该包装的工作原理是:对每个添加或移除操作锁定整个集合。因此,每个尝试访问集合的线程必须一直等待,直到轮到它来获取锁。这是无法进行伸缩的,并且对于大型集合而言,将会导致性能显著降低。此外,这一设计并不能完全防止出现争用情况。
在 System.Collections.Generic 命名空间中 .NET Framework 2.0 中引入的集合类。这些集合类包括 List<(Of <(T>)>)、Dictionary<(Of <(TKey, TValue>)>) 等。与 .NET Framework 1.0 类相比,这些类提供的类型安全性和性能会更高。不过,.NET Framework 2.0 集合类不提供任何线程同步;当同时在多个线程上添加或移除项时,用户代码必须提供所有同步。
枚举整个集合本质上不是一个线程安全的过程。若要确保枚举过程中的线程安全性,可以在整个枚举过程中锁定集合。若要允许多个线程访问集合以进行读写操作,则必须实现自己的同步或使用 System.Collections.Concurrent 命名空间中的线程安全集合类之一。System.Collections.Concurrent..::.ConcurrentQueue<(Of <(T>)>) 和 System.Collections.Concurrent..::.ConcurrentStack<(Of <(T>)>) 类在枚举元素之前将为元素拍摄快照,以防止转变成另一个线程上的集合。System.Collections.Concurrent..::.ConcurrentDictionary<(Of <(TKey, TValue>)>) 类不拍摄快照。
System.Collections.Concurrent..::.BlockingCollection<(Of <(T>)>) 类提供一个名为 GetConsumingEnumerable 的枚举器方法,该方法通过在枚举项时将项从集合中移除来转变集合。
那么,说到底,到底如何来实现自己的线程安全呢。可以提供一个static object synObj = new object(); 然后lock(synObj)。
3:选择集合类
考虑以下问题:
-
您是否需要一个序列列表,其中的元素通常在检索其值后被放弃?
-
如果需要,那么在需要先进先出 (FIFO) 行为时请考虑使用 Queue 类或 Queue<(Of <(T>)>) 泛型类。在需要后进先出 (LIFO) 行为时请考虑使用 Stack 类或 Stack<(Of <(T>)>) 泛型类。若要从多个线程进行安全访问,请使用并发版本 ConcurrentQueue<(Of <(T>)>) 和 ConcurrentStack<(Of <(T>)>)。
-
如果不需要,请考虑使用其他集合。
-
-
是否需要以某种顺序访问元素,例如 FIFO、LIFO 或随机访问?
-
Queue 类和 Queue<(Of <(T>)>) 或 ConcurrentQueue<(Of <(T>)>) 泛型类提供 FIFO 访问。有关更多信息,请参见何时使用线程安全集合。
-
Stack 类和 Stack<(Of <(T>)>) 或 ConcurrentStack<(Of <(T>)>) 泛型类提供 LIFO 访问。有关更多信息,请参见何时使用线程安全集合。
-
LinkedList<(Of <(T>)>) 泛型类允许从开头到末尾或从末尾到开头按顺序访问。
-
-
是否需要通过索引访问每一元素?
-
ArrayList 和 StringCollection 类以及 List<(Of <(T>)>) 泛型类通过元素的从零开始的索引提供对元素的访问。
-
Hashtable、SortedList、ListDictionary 和 StringDictionary 类以及 Dictionary<(Of <(TKey, TValue>)>) 和 SortedDictionary<(Of <(TKey, TValue>)>) 泛型类通过元素的键提供对元素的访问。
-
NameObjectCollectionBase 和 NameValueCollection 类以及 KeyedCollection<(Of <(TKey, TItem>)>) 和 SortedList<(Of <(TKey, TValue>)>) 泛型类通过其元素的从零开始的索引或者通过其元素的键提供对元素的访问。
-
-
每一元素将包含一个值、一个键和一个值的组合还是一个键和多个值的组合?
-
一个值:使用任何基于 IList 接口或 IList<(Of <(T>)>) 泛型接口的集合。
-
一个键和一个值:使用任何基于 IDictionary 接口或 IDictionary<(Of <(TKey, TValue>)>) 泛型接口的集合。
-
带有嵌入的键的一个值:使用 KeyedCollection<(Of <(TKey, TItem>)>) 泛型类。
-
一个键和多个值:使用 NameValueCollection 类。
-
-
是否需要用与输入元素方式不同的方式对元素排序?
-
Hashtable 类按其元素的哈希代码对元素排序。
-
SortedList 类以及 SortedDictionary<(Of <(TKey, TValue>)>) 和 SortedList<(Of <(TKey, TValue>)>) 泛型类根据 IComparer 接口和 IComparer<(Of <(T>)>) 泛型接口的实现按键对元素排序。
-
ArrayList 提供 Sort 方法,该方法接受 IComparer 实现作为参数。其对应的泛型类(List<(Of <(T>)>) 泛型类)提供 Sort 方法,该方法接受 IComparer<(Of <(T>)>) 泛型接口的实现作为参数。
-
-
是否需要信息的快速搜索和检索?
-
对于小集合(10 项或更少),ListDictionary 比 Hashtable 快。Dictionary<(Of <(TKey, TValue>)>) 泛型类提供比 SortedDictionary<(Of <(TKey, TValue>)>) 泛型类更快的查找。多线程实现是 ConcurrentDictionary<(Of <(TKey, TValue>)>)。ConcurrentBag<(Of <(T>)>) 提供针对未排序的数据的快速多线程插入。有关这两种多线程类型的更多信息,请参见何时使用线程安全集合。
-
-
是否需要只接受字符串的集合?
-
StringCollection(基于 IList)和 StringDictionary(基于 IDictionary)都位于 System.Collections.Specialized 命名空间中。
-
此外,通过为泛型类型参数指定 String 类,可以使用 System.Collections.Generic 命名空间中的任何泛型集合类作为强类型字符串集合。
-