# 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
#泛型集合
#集合
#进阶
