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

8.1 KiB
Raw Blame History

List

概念: List<T> 是 C# 提供的==泛型集合类== 本质上可以理解为:==一个长度可变的泛型数组==

它可以:

  1. 按下标访问元素
  2. 自动扩容
  3. 方便地增删查改
  4. 保证集合中元素类型统一

命名空间:

using System.Collections.Generic;

简单理解: 数组长度固定 List<T> 长度可以动态变化 而且比 ArrayList 更安全,因为它有明确类型


通常用法

  1. 存储数量不固定的一组同类型数据
  2. 需要频繁添加、删除、遍历元素
  3. 作为项目里最常见的基础集合之一
  4. 替代早期非泛型集合 ArrayList

实际开发中: 新项目里 List<T> 的使用频率非常高 很多时候它就是“默认列表容器”


语法

创建:

List<int> list = new List<int>();

创建并赋初值:

List<string> names = new List<string>() { "小明", "小红", "小刚" };

添加:

list.Add(10);

访问:

list[0]

数量:

list.Count

基础案例

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);

输出:

10
20
30
3

理解:

  1. List<int> 里只能放 int
  2. 可以像数组一样通过下标访问
  3. Count 表示当前实际元素个数

和 ArrayList 的区别

ArrayList 内部按 object 存储 可以混合存很多类型 取值时常常需要强制类型转换 值类型放进去会发生装箱拆箱

List<T>: 内部是泛型集合 类型更明确 编译期就能检查类型错误 通常不会像 ArrayList 那样频繁发生装箱拆箱

对比表:

对比点 ArrayList List<T>
类型 非泛型 泛型
存储 object 指定类型 T
类型安全 较弱 更强
取值 常要强转 一般不用强转
性能 值类型可能有装箱拆箱 通常更好
推荐程度 主要用于学习旧集合 实际开发常用

增删查改

增加:

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 });

删除:

list.Remove(2);    // 删除第一个匹配到的元素
list.RemoveAt(0);  // 删除指定下标元素
list.Clear();      // 清空整个列表

查找:

bool hasValue = list.Contains(99);
int index1 = list.IndexOf(99);
int index2 = list.LastIndexOf(99);

修改:

list[0] = 100;

遍历

使用 for

List<string> names = new List<string>() { "A", "B", "C" };

for (int i = 0; i < names.Count; i++)
{
	Console.WriteLine(names[i]);
}

使用 foreach

foreach (string item in names)
{
	Console.WriteLine(item);
}

说明: 如果只是读取元素,foreach 写起来更方便 如果需要下标,通常用 for


Count 和 Capacity

Count 当前实际有多少个元素

Capacity 内部数组当前能容纳多少个元素

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


常用成员

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():反转

排序和反转

排序:

List<int> list = new List<int>() { 3, 5, 1, 2 };
list.Sort();

结果:

1 2 3 5

反转:

list.Reverse();

结果:

5 3 2 1

说明: Sort() 默认按从小到大排序 Reverse() 是把当前顺序直接反过来


插入和删除时要注意下标

错误示例:

List<int> list = new List<int>();
list.Add(10);

// list[1] = 20;
// list.RemoveAt(5);

原因: 下标越界会报错

更稳的写法:

if (list.Count > 0)
{
	Console.WriteLine(list[0]);
}

说明: List<T> 虽然长度可变 但访问下标时仍然必须合法


泛型的好处

List<int> numbers = new List<int>();
numbers.Add(10);
// numbers.Add("hello");

说明: List<int> 只能存 int"hello" 这种类型不匹配的值,编译阶段就会报错

优点:

  1. 类型安全
  2. 减少强制类型转换
  3. 代码提示更清楚
  4. 更适合实际开发

基础案例:怪物列表

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");
	}
}

输出:

Boss Atk
Goblin Atk
Boss Atk
Goblin Atk

理解:

  1. List<Monster> 用来统一保存所有怪物对象
  2. 因为 BossGoblin 都继承 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. 下标访问、InsertRemoveAt 都要注意越界

错误示例:

List<int> list = new List<int>() { 10, 20, 30 };

// Console.WriteLine(list[3]);

原因: 下标从 0 开始 上面这个列表只有 012 三个有效下标


一句话记忆

==List 是长度可变、类型安全、最常用的泛型列表集合。==


面试/复习时怎么说

可以这样答:

List<T> 是 C# 中非常常用的泛型集合,本质上可以理解成长度可变的数组。
它支持按下标访问,也支持方便的增删查改,比如 AddInsertRemoveRemoveAt
相比 ArrayListList<T> 类型更安全,通常不需要强制类型转换,所以在实际开发中更常用。”


引用:

  1. Microsoft Learn: List Class
  2. Microsoft Learn: System.Collections.Generic Namespace

#List #泛型集合 #集合 #进阶