SerializableAttribute]
public class List<T> : IList<T>, ICollection<T>,
IEnumerable<T>, IList, ICollection, IEnumerable
类型参数
-
T
列表中元素的类型。
List 类是 ArrayList 类的泛型等效类。该类使用大小可按需动态增加的数组实现 IList 泛型接口。
List 类既使用相等比较器又使用排序比较器。
-
诸如 Contains、IndexOf、LastIndexOf 和 Remove 这样的方法对列表元素使用相等比较器。类型 T 的默认相等比较器按如下方式确定。如果类型 T 实现 IEquatable 泛型接口,则相等比较器为该接口的 Equals 方法;否则,默认相等比较器为 Object.Equals(Object)。
-
诸如 BinarySearch 和 Sort 这样的方法对列表元素使用排序比较器。类型 T 的默认比较器按如下方式确定。如果类型 T 实现 IComparable 泛型接口,则默认比较器为该接口的 CompareTo 方法;否则,如果类型 T 实现非泛型 IComparable 接口,则默认比较器为该接口的 CompareTo 方法。如果类型 T 没有实现其中任一个接口,则不存在默认比较器,并且必须显式提供比较器或比较委托。
List 不保证是排序的。在执行要求 List 已排序的操作(例如 BinarySearch)之前,您必须对 List 进行排序。
可使用一个整数索引访问此集合中的元素。此集合中的索引从零开始。
List 接受 空引用(在 Visual Basic 中为 Nothing) 作为引用类型的有效值并且允许有重复的元素。
性能注意事项
在决定使用 List 还是使用 ArrayList 类(两者具有类似的功能)时,记住 List 类在大多数情况下执行得更好并且是类型安全的。如果对 List 类的类型 T 使用引用类型,则两个类的行为是完全相同的。但是,如果对类型 T 使用值类型,则需要考虑实现和装箱问题。
如果对类型 T 使用值类型,则编译器将特别针对该值类型生成 List 类的实现。这意味着不必对 List 对象的列表元素进行装箱就可以使用该元素,并且在创建大约 500 个列表元素之后,不对列表元素装箱所节省的内存将大于生成该类实现所使用的内存。
确保用于类型 T 的值类型实现 IEquatable 泛型接口。如果未实现,则诸如 Contains 这样的方法必须调用 Object.Equals(Object) 方法,后者对受影响的列表元素进行装箱。如果值类型实现 IComparable 接口,并且您拥有源代码,则还应实现 IComparable 泛型接口以防止 BinarySearch 和 Sort 方法对列表元素进行装箱。如果您不拥有源代码,则将一个 IComparer 对象传递给 BinarySearch 和 Sort 方法。
使用 List 类的特定于类型的实现,而不是使用 ArrayList 类或自己编写强类型包装集合,这样是很有好处的。原因是您的实现必须做 .NET Framework 已经为您完成的工作,并且公共语言运行库能够共享 Microsoft 中间语言代码和元素据,这是您的实现所无法做到的。
该示例使用 Contains 方法测试该列表中是否存在某个项,使用 Insert 方法在列表的中间插入一个新项,然后再次显示列表的内容。
默认的 Item 属性(C# 中的索引器)用于检索项,Remove 方法用于移除前面添加的重复项的第一个实例,然后,该示例再次显示内容。Remove 方法总是移除它所遇到的第一个实例。
该示例使用 TrimExcess 方法减小容量以匹配计数,然后显示 Capacity 和 Count 属性。如果未用容量已经小于总容量的 10%,则列表容量不会进行调整。
最后,代码示例使用 Clear 方法移除列表中的所有项,并显示 Capacity 和 Count 属性。
C#
|
using System; using System.Collections.Generic; public class Example { public static void Main() { List<string> dinosaurs = new List<string>(); Console.WriteLine("/nCapacity: {0}", dinosaurs.Capacity); dinosaurs.Add("Tyrannosaurus"); dinosaurs.Add("Amargasaurus"); dinosaurs.Add("Mamenchisaurus"); dinosaurs.Add("Deinonychus"); dinosaurs.Add("Compsognathus"); Console.WriteLine(); foreach(string dinosaur in dinosaurs) { Console.WriteLine(dinosaur); } Console.WriteLine("/nCapacity: {0}", dinosaurs.Capacity); Console.WriteLine("Count: {0}", dinosaurs.Count); Console.WriteLine("/nContains(/"Deinonychus/"): {0}", dinosaurs.Contains("Deinonychus")); Console.WriteLine("/nInsert(2, /"Compsognathus/")"); dinosaurs.Insert(2, "Compsognathus"); Console.WriteLine(); foreach(string dinosaur in dinosaurs) { Console.WriteLine(dinosaur); } Console.WriteLine("/ndinosaurs[3]: {0}", dinosaurs[3]); Console.WriteLine("/nRemove(/"Compsognathus/")"); dinosaurs.Remove("Compsognathus"); Console.WriteLine(); foreach(string dinosaur in dinosaurs) { Console.WriteLine(dinosaur); } dinosaurs.TrimExcess(); Console.WriteLine("/nTrimExcess()"); Console.WriteLine("Capacity: {0}", dinosaurs.Capacity); Console.WriteLine("Count: {0}", dinosaurs.Count); dinosaurs.Clear(); Console.WriteLine("/nClear()"); Console.WriteLine("Capacity: {0}", dinosaurs.Capacity); Console.WriteLine("Count: {0}", dinosaurs.Count); } } /* This code example produces the following output: Capacity: 0 Tyrannosaurus Amargasaurus Mamenchisaurus Deinonychus Compsognathus Capacity: 8 Count: 5 Contains("Deinonychus"): True Insert(2, "Compsognathus") Tyrannosaurus Amargasaurus Compsognathus Mamenchisaurus Deinonychus Compsognathus dinosaurs[3]: Mamenchisaurus Remove("Compsognathus") Tyrannosaurus Amargasaurus Mamenchisaurus Deinonychus Compsognathus TrimExcess() Capacity: 5 Count: 5 Clear() Capacity: 5 Count: 0 */ |
线程安全
此类型的公共静态(在 Visual Basic 中为 Shared)成员是线程安全的。但不能保证任何实例成员是线程安全的。
只要不修改该集合,List 就可以同时支持多个阅读器。通过集合枚举在本质上不是一个线程安全的过程。在枚举与一个或多个写访问竞争的罕见情况下,确保线程安全的唯一方法是在整个枚举期间锁定集合。若要允许多个线程访问集合以进行读写操作,则必须实现自己的同步。
# 2.0 中加入了泛型,只是与 C++ 的泛型相差极多,只具有最简单的泛化
而以,像 STL 重头戏的 traits 是无法使用的了(不过 sf.net 上有一个 CSTL
(C# STL)的项目,初具形状)。
.NET 2.0 加入了一系列的泛型容器,如 List<T>,Dictionary<Key,
Value> 等。这些容器对于引用(Object 及子类)类型和原生数据类型(如 int,
double 等)支持得很好,然而对 struct 类型的元素的支持却有问题。这里所说
的问题是指,List<T> 并不能对等的代替 T[]。struct 类型的问题在于,你不能
直接修改 List<struct T> 中元素的成员。
产生问题的原因在于,C# 只有针对“对象”的引用,而没有 C++ 那样针对
值类型的引用。struct T 是值类型,所以你在索引 List<struct T> 元素的时候,
返回的只是元素的一个值拷贝。自然,你对这个拷贝的成员的修改不会影响到真
实的元素。幸运的是,C# 编译器禁止这种操作。代码如下:
struct T
{
public int a;
}
List<T> list;
list.Add(new T());
list[0].a = 100; // 编译错误!!
// 实际你要这样做:
T t = list[0];
t.a = 100;
list[0] = t;
在上面的代码里,我的解决办法是复制到一个临时变量,修改值,然后回写。
为什么不能修改真实的元素,考虑如下代码:
public T this[int index]
{
get
{
T t = list[index];
return t; // 返回的是拷贝,不是吗?
}
}