8.1 KiB
List
概念:
List<T> 是 C# 提供的==泛型集合类==
本质上可以理解为:==一个长度可变的泛型数组==
它可以:
- 按下标访问元素
- 自动扩容
- 方便地增删查改
- 保证集合中元素类型统一
命名空间:
using System.Collections.Generic;
简单理解:
数组长度固定
List<T> 长度可以动态变化
而且比 ArrayList 更安全,因为它有明确类型
通常用法
- 存储数量不固定的一组同类型数据
- 需要频繁添加、删除、遍历元素
- 作为项目里最常见的基础集合之一
- 替代早期非泛型集合
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
理解:
List<int>里只能放int- 可以像数组一样通过下标访问
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();
常用方法:
Add(T item):添加一个元素AddRange(IEnumerable<T>):添加一组元素Insert(int index, T item):插入元素Remove(T item):删除第一个匹配项RemoveAt(int index):按下标删除Clear():清空Contains(T item):判断是否存在IndexOf(T item):正向查找下标LastIndexOf(T item):反向查找下标Sort():排序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" 这种类型不匹配的值,编译阶段就会报错
优点:
- 类型安全
- 减少强制类型转换
- 代码提示更清楚
- 更适合实际开发
基础案例:怪物列表
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
理解:
List<Monster>用来统一保存所有怪物对象- 因为
Boss和Goblin都继承Monster - 所以它们都能放进
List<Monster>中 - 再通过遍历统一调用攻击方法
适合场景
- 玩家背包中的物品列表
- 敌人列表、角色列表、任务列表
- 聊天消息列表
- 成绩列表、订单列表、日志列表
一句话:
只要你需要“数量不固定的一组同类型数据”
通常都可以先考虑 List<T>
注意
List<T>在System.Collections.Generic命名空间中List<T>是泛型集合,T表示元素类型Count是实际元素个数,不是容量Capacity是当前容量,可能大于CountAdd是追加到末尾Insert是插入到指定位置Remove删除的是第一个匹配项RemoveAt删除的是指定下标元素List<T>很常用,但按下标中间插入/删除很多次时会有移动开销
易错点
List<T>是类,不是数组,但用法上和数组有点像Count不是LengthCapacity不是当前元素数量foreach遍历时一般不要直接改集合内容- 下标访问、
Insert、RemoveAt都要注意越界
错误示例:
List<int> list = new List<int>() { 10, 20, 30 };
// Console.WriteLine(list[3]);
原因:
下标从 0 开始
上面这个列表只有 0、1、2 三个有效下标
一句话记忆
==List 是长度可变、类型安全、最常用的泛型列表集合。==
面试/复习时怎么说
可以这样答:
“List<T> 是 C# 中非常常用的泛型集合,本质上可以理解成长度可变的数组。
它支持按下标访问,也支持方便的增删查改,比如 Add、Insert、Remove、RemoveAt。
相比 ArrayList,List<T> 类型更安全,通常不需要强制类型转换,所以在实际开发中更常用。”
引用:
- Microsoft Learn: List Class
- Microsoft Learn: System.Collections.Generic Namespace
#List #泛型集合 #集合 #进阶