C# 集合详解:ArrayList 与 List<T>的核心用法与对比
一、C# 集合体系概览
C# 的集合类型主要分布在两个核心命名空间:
- System.Collections:非泛型集合,适用于.NET 早期版本,代表类型有
ArrayList、Hashtable、Queue等; - System.Collections.Generic:泛型集合(.NET 2.0+),类型安全且性能更优,代表类型有
List<T>、Dictionary<TKey, TValue>、HashSet<T>等。
其中,ArrayList和List<T>作为动态数组的典型实现,是日常开发中最常用的集合类型,也是理解 C# 集合设计思想的关键。
二、ArrayList:非泛型动态数组
2.1 ArrayList 核心特性
ArrayList是 C# 早期的动态数组实现,本质是封装了object[]数组的类,核心特性如下:
- 动态扩容:底层基于数组实现,初始容量为 0,添加第一个元素时扩容至 4,后续自动按需扩容;
- 非类型安全:所有元素均以
object类型存储,值类型会发生装箱 / 拆箱操作; - 灵活存储:允许存储
null值、重复元素,支持任意类型的对象; - 索引访问:支持数组式的索引器访问,时间复杂度 O (1)。
2.2 ArrayList 常用操作
1. 初始化
ArrayList提供三种初始化方式,适配不同场景:
// 空列表(初始容量0) ArrayList list1 = new ArrayList(); // 指定初始容量(减少扩容次数,提升性能) ArrayList list2 = new ArrayList(100); // 从其他集合初始化 ArrayList list3 = new ArrayList(new int[] { 1, 2, 3 });2. 元素增删
ArrayList提供丰富的元素操作方法,覆盖单个 / 批量、尾部 / 指定位置的增删需求:
ArrayList list = new ArrayList(); // 1. 添加元素 list.Add("C#"); // 尾部添加单个元素 list.Add(123); // 值类型自动装箱(int → object) list.AddRange(new object[] { 4.5, DateTime.Now }); // 批量添加 list.Insert(1, "插入到索引1的位置"); // 指定索引插入 list.InsertRange(2, new ArrayList { "a", "b" }); // 批量插入 // 2. 删除元素 list.Remove("C#"); // 按值删除第一个匹配项 list.RemoveAt(0); // 按索引删除 list.RemoveRange(1, 2); // 从索引1开始删除2个元素 list.Clear(); // 清空所有元素3. 元素查找与访问
ArrayList list = new ArrayList { "A", "B", "C", "B" }; // 检查元素是否存在 bool hasB = list.Contains("B"); // true // 查找元素索引 int firstBIndex = list.IndexOf("B"); // 1 int lastBIndex = list.LastIndexOf("B"); // 3 // 索引器访问(需显式类型转换) string item = (string)list[0]; // "A" // 排序后二分查找(需先Sort) list.Sort(); int cIndex = list.BinarySearch("C"); // 22.3 ArrayList 的局限性
尽管ArrayList灵活,但存在明显短板:
- 性能损耗:值类型的装箱 / 拆箱操作增加内存开销和性能消耗;
- 类型不安全:编译时无法校验元素类型,运行时可能抛出
InvalidCastException; - 现代开发不推荐:仅适用于遗留代码或.NET 1.x 环境,新代码优先使用泛型集合。
三、List<T>:类型安全的泛型动态数组
List<T>是ArrayList的泛型替代版本,解决了非泛型集合的类型安全和性能问题,是当前 C# 开发的首选动态数组。
3.1 List<T>核心优势
与ArrayList相比,List<T>的核心改进在于:
| 特性 | ArrayList | List<T> |
|---|---|---|
| 类型安全 | 非泛型,object 存储 | 泛型,编译时类型校验 |
| 性能 | 装箱 / 拆箱损耗 | 无装箱操作,性能更优 |
| 内存占用 | 较高(object 引用) | 更低(直接存储值类型) |
| 错误排查 | 运行时类型转换异常 | 编译时类型错误提示 |
3.2 List<T>常用操作
1. 初始化与基础操作
// 空列表 List<int> numbers = new List<int>(); // 初始化赋值 List<string> names = new List<string> { "Alice", "Bob" }; // 指定初始容量 List<double> values = new List<double>(10); // 添加元素 numbers.Add(1); numbers.AddRange(new int[] { 2, 3, 4 }); // 访问与修改 int first = numbers[0]; // 无需拆箱 numbers[1] = 10; // 基础查询 int count = numbers.Count; bool has3 = numbers.Contains(3);2. 高级操作:排序
List<T>的Sort方法支持多种排序方式,满足复杂场景需求:
方式 1:默认排序(实现 IComparable<T>)
自定义类型需实现IComparable<T>接口,重写CompareTo方法:
public class Student : IComparable<Student> { public int Id { get; set; } public string Name { get; set; } public int Age { get; set; } // 按Id升序排序 public int CompareTo(Student other) { if (this.Id > other.Id) return 1; if (this.Id < other.Id) return -1; return 0; } } // 排序使用 List<Student> students = new List<Student> { new Student { Id = 1, Name = "张三" }, new Student { Id = 3, Name = "李四" }, new Student { Id = 2, Name = "王五" } }; students.Sort(); // 按Id升序排列方式 2:自定义比较器(IComparer<T>)
适用于多种排序规则的场景,无需修改实体类:
// 按年龄降序的比较器 public class StudentAgeComparer : IComparer<Student> { public int Compare(Student x, Student y) { return y.Age.CompareTo(x.Age); } } // 使用比较器排序 students.Sort(new StudentAgeComparer());方式 3:Lambda 表达式(Comparison<T>)
适用于临时排序需求,简化代码:
// 按年龄降序排序 students.Sort((x, y) => y.Age.CompareTo(x.Age));3. 高级操作:查找与转换
List<int> numbers = new List<int> { 1, 2, 3, 4, 5 }; // 查找第一个偶数 int firstEven = numbers.Find(n => n % 2 == 0); // 查找所有偶数 List<int> allEvens = numbers.FindAll(n => n % 2 == 0); // 转换为字符串列表 List<string> strNumbers = numbers.ConvertAll(n => n.ToString()); // 转换为数组 int[] numArray = numbers.ToArray();四、ArrayList 与 List<T>核心对比
表格
| 维度 | ArrayList | List<T> |
|---|---|---|
| 类型系统 | 非泛型(object) | 泛型(强类型) |
| 性能 | 装箱 / 拆箱损耗 | 无装箱,性能更高 |
| 类型安全 | 运行时可能出错 | 编译时类型校验 |
| 内存占用 | 较高 | 更低 |
| 适用场景 | 遗留代码、混合类型存储 | 现代开发、类型明确的集合 |
| 引入版本 | .NET 1.0 | .NET 2.0+ |
五、最佳实践
- 优先使用 List<T>:新开发项目中,无论值类型还是引用类型,均推荐使用
List<T>,兼顾性能与类型安全; - 避免 ArrayList:仅在维护.NET 1.x 遗留代码或必须存储混合类型时使用
ArrayList; - 指定初始容量:当已知集合大致大小,初始化时指定容量(如
new List<int>(100)),减少自动扩容的性能损耗; - 排序优化:自定义类型排序优先使用
IComparer<T>或Comparison<T>,避免修改实体类的IComparable<T>实现; - 减少装箱操作:若需存储混合类型,可使用
List<object>替代ArrayList,保持泛型特性。
总结
ArrayList作为 C# 早期的动态数组实现,见证了.NET 集合体系的发展;而List<T>作为泛型时代的产物,凭借类型安全、高性能的优势成为现代 C# 开发的首选。理解两者的设计思想与使用差异,不仅能帮助开发者写出更高效的代码,也能深入掌握 C# 类型系统的核心逻辑。在实际开发中,结合场景选择合适的集合类型,才能充分发挥 C# 的语言特性与性能优势。
