C# 动态数组(ArrayList)(超详细)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;演示链接: http://116.62.199.48:7070 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 90w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 3100+ 小伙伴加入学习 ,欢迎点击围观
前言
在编程世界中,数组是一个基础且强大的数据结构,但传统的静态数组在容量固定这一特性上往往限制了灵活性。例如,当需要频繁添加或删除元素时,静态数组的容量不足或浪费问题会变得尤为突出。为此,C# 提供了 动态数组(ArrayList),它如同一个“可伸缩的容器”,能够根据实际需求自动调整容量,为开发者提供了更便捷的操作体验。本文将深入讲解 C# 动态数组(ArrayList) 的核心概念、使用场景,并通过实际案例帮助读者掌握其核心功能。
一、动态数组(ArrayList)的基础概念
1.1 静态数组的局限性
传统静态数组在声明时需要预先指定容量,例如:
int[] numbers = new int[5];
一旦数组被初始化,其容量便无法更改。若需要添加第 6 个元素,必须创建新数组并复制原有数据,这显然效率低下。
1.2 动态数组(ArrayList)的优势
ArrayList 是 C# 中 System.Collections
命名空间下的一个集合类,其核心特性如下:
- 动态扩容:当元素数量超过当前容量时,数组会自动扩展;
- 存储任意类型:支持存储
object
类型元素,可通过类型转换实现多类型数据的存储; - 灵活操作:提供增删改查等丰富方法,如
Add()
、Remove()
、IndexOf()
等。
形象比喻:
可以将 ArrayList 想象为一个“可无限扩展的抽屉柜”——每个抽屉(元素)的大小固定,但整个柜子(数组)的高度(容量)会随着抽屉数量的增加而自动延长,无需手动搬运所有抽屉到更大的柜子中。
二、动态数组(ArrayList)的常用操作
2.1 创建与初始化
通过以下方式创建 ArrayList 实例:
using System.Collections;
ArrayList list = new ArrayList();
// 或直接初始化
ArrayList list = new ArrayList { 10, "Hello", 3.14 };
2.2 添加元素
使用 Add()
方法向数组末尾添加元素:
list.Add(20); // 添加整数
list.Add("World"); // 添加字符串
list.Add(new DateTime(2023, 10, 1)); // 添加对象
2.3 访问与修改元素
通过索引访问元素时,需注意 ArrayList 的索引从 0 开始:
int firstElement = (int)list[0]; // 强制类型转换
list[1] = "C# Developer"; // 修改第二个元素
2.4 删除元素
可通过 Remove()
方法或索引直接删除:
list.Remove("Hello"); // 按值删除
list.RemoveAt(2); // 按索引删除
三、动态数组(ArrayList)的核心属性与方法
3.1 核心属性
属性名 | 描述 |
---|---|
Capacity | 当前数组的物理容量(可容纳的最大元素数,未满时可能超过实际元素数量) |
Count | 当前数组中的实际元素数量 |
关键区别:
- Capacity 是“准备好的空间”,而 Count 是“实际使用的空间”。例如,若
Capacity
为 10,但Count
为 3,则表示数组已预留 10 个位置,但仅使用了前 3 个。
3.2 扩展容量的机制
当 Count
接近 Capacity
时,ArrayList 会自动扩容,默认扩容为原容量的 1.5 倍(向下取整后加 1)。例如,原容量为 4 时,扩容后变为 6。
3.3 其他常用方法
方法 | 作用 |
---|---|
Clear() | 清空所有元素 |
Contains() | 检查数组是否包含指定元素 |
Sort() | 对元素进行排序(需类型实现 IComparable 接口) |
ToArray() | 将数组转换为静态数组 |
四、动态数组(ArrayList)的实际应用案例
4.1 案例 1:学生信息管理系统
假设需要动态存储学生姓名、年龄和成绩:
ArrayList students = new ArrayList();
// 添加学生信息(对象形式)
students.Add(new { Name = "Alice", Age = 20, Score = 90 });
students.Add(new { Name = "Bob", Age = 22, Score = 85 });
// 遍历输出学生信息
foreach (var student in students)
{
dynamic s = student;
Console.WriteLine($"姓名:{s.Name}, 年龄:{s.Age}, 成绩:{s.Score}");
}
4.2 案例 2:动态统计用户输入
用户输入数字,程序自动存储并计算总和:
ArrayList numbers = new ArrayList();
int sum = 0;
while (true)
{
Console.Write("请输入一个整数(输入-1结束):");
int input = int.Parse(Console.ReadLine());
if (input == -1) break;
numbers.Add(input);
sum += input;
}
Console.WriteLine($"总和为:{sum}");
五、动态数组(ArrayList)的性能与局限性
5.1 性能分析
- 插入/删除操作:在末尾添加元素的时间复杂度为 O(1)(未触发扩容时),但扩容时可能达到 O(n);
- 查找操作:通过索引访问元素的时间复杂度为 O(1)。
5.2 局限性与替代方案
尽管 ArrayList 灵活,但存在以下不足:
- 类型安全性:存储的元素均为
object
类型,需频繁进行类型转换; - 性能损耗:类型转换和装箱/拆箱操作可能影响性能;
- 泛型替代:C# 推荐使用 泛型集合 List
,它解决了类型安全问题并优化了性能。
对比示例:
// ArrayList vs List<int>
ArrayList arrayList = new ArrayList(); // 需强制转换
List<int> genericList = new List<int>(); // 直接操作int类型
六、动态数组(ArrayList)的进阶技巧
6.1 容量预分配
若已知元素数量范围,可通过 Capacity
属性预分配空间,避免频繁扩容:
ArrayList list = new ArrayList();
list.Capacity = 100; // 初始容量设为100
6.2 遍历与排序
通过 foreach
遍历或 Sort()
方法排序:
// 排序整数
ArrayList numbers = new ArrayList { 5, 3, 8, 1 };
numbers.Sort(); // 排序后为 [1, 3, 5, 8]
6.3 转换为静态数组
当需要固定容量时,可将 ArrayList 转换为静态数组:
int[] fixedArray = (int[])list.ToArray(typeof(int));
结论
C# 动态数组(ArrayList) 是一种灵活且功能强大的数据结构,尤其适合需要动态调整容量的场景。尽管它存在类型安全性和性能上的局限性,但在需要快速实现基础功能或兼容旧代码时,仍是一个值得掌握的工具。对于更复杂的需求,建议结合泛型集合(如 List<T>
)以获得更好的类型安全和性能表现。
通过本文的讲解与案例,希望读者能够掌握 ArrayList 的核心用法,并在实际开发中根据场景灵活选择合适的数据结构,提升代码的效率与可维护性。