C# 动态数组(ArrayList)(超详细)

更新时间:

💡一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论

截止目前, 星球 内专栏累计输出 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 灵活,但存在以下不足:

  1. 类型安全性:存储的元素均为 object 类型,需频繁进行类型转换;
  2. 性能损耗:类型转换和装箱/拆箱操作可能影响性能;
  3. 泛型替代: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 的核心用法,并在实际开发中根据场景灵活选择合适的数据结构,提升代码的效率与可维护性。

最新发布