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"); // 2
2.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# 的语言特性与性能优势。
更多推荐

所有评论(0)