Files
My-Notes/笔记/CSharp/进阶/List.md
T
2026-06-22 22:11:42 +08:00

424 lines
8.1 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# List
概念:
`List<T>` 是 C# 提供的==泛型集合类==
本质上可以理解为:==一个长度可变的泛型数组==
它可以:
1. 按下标访问元素
2. 自动扩容
3. 方便地增删查改
4. 保证集合中元素类型统一
命名空间:
```csharp
using System.Collections.Generic;
```
简单理解:
数组长度固定
`List<T>` 长度可以动态变化
而且比 `ArrayList` 更安全,因为它有明确类型
----
# 通常用法
1. 存储数量不固定的一组同类型数据
2. 需要频繁添加、删除、遍历元素
3. 作为项目里最常见的基础集合之一
4. 替代早期非泛型集合 `ArrayList`
实际开发中:
新项目里 `List<T>` 的使用频率非常高
很多时候它就是“默认列表容器”
----
# 语法
创建:
```csharp
List<int> list = new List<int>();
```
创建并赋初值:
```csharp
List<string> names = new List<string>() { "小明", "小红", "小刚" };
```
添加:
```csharp
list.Add(10);
```
访问:
```csharp
list[0]
```
数量:
```csharp
list.Count
```
----
# 基础案例
```csharp
using System;
using System.Collections.Generic;
List<int> list = new List<int>();
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>` 里只能放 `int`
2. 可以像数组一样通过下标访问
3. `Count` 表示当前实际元素个数
----
# 和 ArrayList 的区别
`ArrayList`
内部按 `object` 存储
可以混合存很多类型
取值时常常需要强制类型转换
值类型放进去会发生装箱拆箱
`List<T>`
内部是泛型集合
类型更明确
编译期就能检查类型错误
通常不会像 `ArrayList` 那样频繁发生装箱拆箱
对比表:
| 对比点 | `ArrayList` | `List<T>` |
|---|---|---|
| 类型 | 非泛型 | 泛型 |
| 存储 | `object` | 指定类型 `T` |
| 类型安全 | 较弱 | 更强 |
| 取值 | 常要强转 | 一般不用强转 |
| 性能 | 值类型可能有装箱拆箱 | 通常更好 |
| 推荐程度 | 主要用于学习旧集合 | 实际开发常用 |
----
# 增删查改
增加:
```csharp
List<int> list = new List<int>();
list.Add(1);
list.Add(2);
list.Add(3);
list.Insert(1, 99);
list.AddRange(new List<int> { 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<string> names = new List<string>() { "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<int> list = new List<int>();
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
list.Add(10);
list.Add(20);
Console.WriteLine(list.Count);
Console.WriteLine(list.Capacity);
```
理解:
`List<T>` 会自动扩容
所以 `Capacity` 往往大于等于 `Count`
----
# 常用成员
```csharp
List<int> list = new List<int>() { 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<T>)`:添加一组元素
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<int> list = new List<int>() { 3, 5, 1, 2 };
list.Sort();
```
结果:
```text
1 2 3 5
```
反转:
```csharp
list.Reverse();
```
结果:
```text
5 3 2 1
```
说明:
`Sort()` 默认按从小到大排序
`Reverse()` 是把当前顺序直接反过来
----
# 插入和删除时要注意下标
错误示例:
```csharp
List<int> list = new List<int>();
list.Add(10);
// list[1] = 20;
// list.RemoveAt(5);
```
原因:
下标越界会报错
更稳的写法:
```csharp
if (list.Count > 0)
{
Console.WriteLine(list[0]);
}
```
说明:
`List<T>` 虽然长度可变
但访问下标时仍然必须合法
----
# 泛型的好处
```csharp
List<int> numbers = new List<int>();
numbers.Add(10);
// numbers.Add("hello");
```
说明:
`List<int>` 只能存 `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<Monster> Monsters = new List<Monster>();
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<Monster>` 用来统一保存所有怪物对象
2. 因为 `Boss``Goblin` 都继承 `Monster`
3. 所以它们都能放进 `List<Monster>`
4. 再通过遍历统一调用攻击方法
----
# 适合场景
1. 玩家背包中的物品列表
2. 敌人列表、角色列表、任务列表
3. 聊天消息列表
4. 成绩列表、订单列表、日志列表
一句话:
只要你需要“数量不固定的一组同类型数据”
通常都可以先考虑 `List<T>`
----
# 注意
1. `List<T>``System.Collections.Generic` 命名空间中
2. `List<T>` 是泛型集合,`T` 表示元素类型
3. `Count` 是实际元素个数,不是容量
4. `Capacity` 是当前容量,可能大于 `Count`
5. `Add` 是追加到末尾
6. `Insert` 是插入到指定位置
7. `Remove` 删除的是第一个匹配项
8. `RemoveAt` 删除的是指定下标元素
9. `List<T>` 很常用,但按下标中间插入/删除很多次时会有移动开销
----
# 易错点
1. `List<T>` 是类,不是数组,但用法上和数组有点像
2. `Count` 不是 `Length`
3. `Capacity` 不是当前元素数量
4. `foreach` 遍历时一般不要直接改集合内容
5. 下标访问、`Insert``RemoveAt` 都要注意越界
错误示例:
```csharp
List<int> list = new List<int>() { 10, 20, 30 };
// Console.WriteLine(list[3]);
```
原因:
下标从 `0` 开始
上面这个列表只有 `0``1``2` 三个有效下标
----
# 一句话记忆
==List<T> 是长度可变、类型安全、最常用的泛型列表集合。==
----
# 面试/复习时怎么说
可以这样答:
`List<T>` 是 C# 中非常常用的泛型集合,本质上可以理解成长度可变的数组。
它支持按下标访问,也支持方便的增删查改,比如 `Add``Insert``Remove``RemoveAt`
相比 `ArrayList``List<T>` 类型更安全,通常不需要强制类型转换,所以在实际开发中更常用。”
----
引用:
1. Microsoft Learn: [List<T> 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
#泛型集合
#集合
#进阶