# List 概念: `List` 是 C# 提供的==泛型集合类== 本质上可以理解为:==一个长度可变的泛型数组== 它可以: 1. 按下标访问元素 2. 自动扩容 3. 方便地增删查改 4. 保证集合中元素类型统一 命名空间: ```csharp using System.Collections.Generic; ``` 简单理解: 数组长度固定 `List` 长度可以动态变化 而且比 `ArrayList` 更安全,因为它有明确类型 ---- # 通常用法 1. 存储数量不固定的一组同类型数据 2. 需要频繁添加、删除、遍历元素 3. 作为项目里最常见的基础集合之一 4. 替代早期非泛型集合 `ArrayList` 实际开发中: 新项目里 `List` 的使用频率非常高 很多时候它就是“默认列表容器” ---- # 语法 创建: ```csharp List list = new List(); ``` 创建并赋初值: ```csharp List names = new List() { "小明", "小红", "小刚" }; ``` 添加: ```csharp list.Add(10); ``` 访问: ```csharp list[0] ``` 数量: ```csharp list.Count ``` ---- # 基础案例 ```csharp using System; using System.Collections.Generic; List list = new List(); list.Add(10); list.Add(20); list.Add(30); Console.WriteLine(list[0]); Console.WriteLine(list[1]); Console.WriteLine(list[2]); Console.WriteLine(list.Count); ``` 输出: ```text 10 20 30 3 ``` 理解: 1. `List` 里只能放 `int` 2. 可以像数组一样通过下标访问 3. `Count` 表示当前实际元素个数 ---- # 和 ArrayList 的区别 `ArrayList`: 内部按 `object` 存储 可以混合存很多类型 取值时常常需要强制类型转换 值类型放进去会发生装箱拆箱 `List`: 内部是泛型集合 类型更明确 编译期就能检查类型错误 通常不会像 `ArrayList` 那样频繁发生装箱拆箱 对比表: | 对比点 | `ArrayList` | `List` | |---|---|---| | 类型 | 非泛型 | 泛型 | | 存储 | `object` | 指定类型 `T` | | 类型安全 | 较弱 | 更强 | | 取值 | 常要强转 | 一般不用强转 | | 性能 | 值类型可能有装箱拆箱 | 通常更好 | | 推荐程度 | 主要用于学习旧集合 | 实际开发常用 | ---- # 增删查改 增加: ```csharp List list = new List(); list.Add(1); list.Add(2); list.Add(3); list.Insert(1, 99); list.AddRange(new List { 7, 8, 9 }); ``` 删除: ```csharp list.Remove(2); // 删除第一个匹配到的元素 list.RemoveAt(0); // 删除指定下标元素 list.Clear(); // 清空整个列表 ``` 查找: ```csharp bool hasValue = list.Contains(99); int index1 = list.IndexOf(99); int index2 = list.LastIndexOf(99); ``` 修改: ```csharp list[0] = 100; ``` ---- # 遍历 使用 `for`: ```csharp List names = new List() { "A", "B", "C" }; for (int i = 0; i < names.Count; i++) { Console.WriteLine(names[i]); } ``` 使用 `foreach`: ```csharp foreach (string item in names) { Console.WriteLine(item); } ``` 说明: 如果只是读取元素,`foreach` 写起来更方便 如果需要下标,通常用 `for` ---- # Count 和 Capacity `Count`: 当前实际有多少个元素 `Capacity`: 内部数组当前能容纳多少个元素 ```csharp List list = new List(); Console.WriteLine(list.Count); Console.WriteLine(list.Capacity); list.Add(10); list.Add(20); Console.WriteLine(list.Count); Console.WriteLine(list.Capacity); ``` 理解: `List` 会自动扩容 所以 `Capacity` 往往大于等于 `Count` ---- # 常用成员 ```csharp List list = new List() { 3, 1, 2, 1 }; Console.WriteLine(list.Count); Console.WriteLine(list.Contains(2)); Console.WriteLine(list.IndexOf(1)); Console.WriteLine(list.LastIndexOf(1)); list.Sort(); list.Reverse(); ``` 常用方法: 1. `Add(T item)`:添加一个元素 2. `AddRange(IEnumerable)`:添加一组元素 3. `Insert(int index, T item)`:插入元素 4. `Remove(T item)`:删除第一个匹配项 5. `RemoveAt(int index)`:按下标删除 6. `Clear()`:清空 7. `Contains(T item)`:判断是否存在 8. `IndexOf(T item)`:正向查找下标 9. `LastIndexOf(T item)`:反向查找下标 10. `Sort()`:排序 11. `Reverse()`:反转 ---- # 排序和反转 排序: ```csharp List list = new List() { 3, 5, 1, 2 }; list.Sort(); ``` 结果: ```text 1 2 3 5 ``` 反转: ```csharp list.Reverse(); ``` 结果: ```text 5 3 2 1 ``` 说明: `Sort()` 默认按从小到大排序 `Reverse()` 是把当前顺序直接反过来 ---- # 插入和删除时要注意下标 错误示例: ```csharp List list = new List(); list.Add(10); // list[1] = 20; // list.RemoveAt(5); ``` 原因: 下标越界会报错 更稳的写法: ```csharp if (list.Count > 0) { Console.WriteLine(list[0]); } ``` 说明: `List` 虽然长度可变 但访问下标时仍然必须合法 ---- # 泛型的好处 ```csharp List numbers = new List(); numbers.Add(10); // numbers.Add("hello"); ``` 说明: `List` 只能存 `int` 像 `"hello"` 这种类型不匹配的值,编译阶段就会报错 优点: 1. 类型安全 2. 减少强制类型转换 3. 代码提示更清楚 4. 更适合实际开发 ---- # 基础案例:怪物列表 ```csharp using System; using System.Collections.Generic; class Program { static void Main(string[] args) { Monster monster1 = new Boss(); Monster monster2 = new Goblin(); Monster monster3 = new Boss(); Monster monster4 = new Goblin(); for (int i = 0; i < Monster.Monsters.Count; i++) { Monster.Monsters[i].Atk(); } } } abstract class Monster { public static List Monsters = new List(); public Monster() { Monsters.Add(this); } public abstract void Atk(); } class Boss : Monster { public override void Atk() { Console.WriteLine("Boss Atk"); } } class Goblin : Monster { public override void Atk() { Console.WriteLine("Goblin Atk"); } } ``` 输出: ```text Boss Atk Goblin Atk Boss Atk Goblin Atk ``` 理解: 1. `List` 用来统一保存所有怪物对象 2. 因为 `Boss` 和 `Goblin` 都继承 `Monster` 3. 所以它们都能放进 `List` 中 4. 再通过遍历统一调用攻击方法 ---- # 适合场景 1. 玩家背包中的物品列表 2. 敌人列表、角色列表、任务列表 3. 聊天消息列表 4. 成绩列表、订单列表、日志列表 一句话: 只要你需要“数量不固定的一组同类型数据” 通常都可以先考虑 `List` ---- # 注意 1. `List` 在 `System.Collections.Generic` 命名空间中 2. `List` 是泛型集合,`T` 表示元素类型 3. `Count` 是实际元素个数,不是容量 4. `Capacity` 是当前容量,可能大于 `Count` 5. `Add` 是追加到末尾 6. `Insert` 是插入到指定位置 7. `Remove` 删除的是第一个匹配项 8. `RemoveAt` 删除的是指定下标元素 9. `List` 很常用,但按下标中间插入/删除很多次时会有移动开销 ---- # 易错点 1. `List` 是类,不是数组,但用法上和数组有点像 2. `Count` 不是 `Length` 3. `Capacity` 不是当前元素数量 4. `foreach` 遍历时一般不要直接改集合内容 5. 下标访问、`Insert`、`RemoveAt` 都要注意越界 错误示例: ```csharp List list = new List() { 10, 20, 30 }; // Console.WriteLine(list[3]); ``` 原因: 下标从 `0` 开始 上面这个列表只有 `0`、`1`、`2` 三个有效下标 ---- # 一句话记忆 ==List 是长度可变、类型安全、最常用的泛型列表集合。== ---- # 面试/复习时怎么说 可以这样答: “`List` 是 C# 中非常常用的泛型集合,本质上可以理解成长度可变的数组。 它支持按下标访问,也支持方便的增删查改,比如 `Add`、`Insert`、`Remove`、`RemoveAt`。 相比 `ArrayList`,`List` 类型更安全,通常不需要强制类型转换,所以在实际开发中更常用。” ---- 引用: 1. Microsoft Learn: [List Class](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic.list-1?view=net-9.0) 2. Microsoft Learn: [System.Collections.Generic Namespace](https://learn.microsoft.com/en-us/dotnet/api/system.collections.generic?view=net-9.0) #List #泛型集合 #集合 #进阶