1. 入门
说实话,数组挺好用的,但它有局限。想改大小?得新建一个数组再手动复制元素!想根据key快速查找?数组会说:"你自己试试看吧"。
现实生活里有很多场景,数组其实不太方便。比如你写个图书馆管理app(hi,我们的小型学习系统!)。一开始你有5本书——数组还够用。过一周变成500本,一个月后有人还书,有人丢书……这时候就轮到集合出场了!
集合就是那种能:
- 自动调整自己大小。
- 支持快速查找、添加、删除。
- 提供更方便的数据处理方式:排序、筛选、批量操作。
.NET里有几个主要的“集合家族”。今天我们要认识最常用的:列表(List<T>)、字典(Dictionary<TKey, TValue>)、集合(HashSet<T>)、队列、栈,还有它们背后的接口。
2. 主要集合类型
虽然我们还没深入泛型集合的具体类型,但你得先知道有哪些主流类别,它们适合什么场景。可以把它们想象成不同的“容器”,每种都有自己的特点:
列表 (Lists)
啥意思? 有序的一串元素。就像购物清单,或者点名册。
核心特点: 元素有顺序(能按下标拿,像数组)。可以有重复元素。大小是动态的。
啥时候用? 当你关心元素顺序,想方便地在末尾加/删元素,或者按位置取元素时。
生活例子: 超市排队、webinar参与者名单、动画帧序列。
字典/映射 (Dictionaries/Maps)
啥意思? 一组“key-value”对。就像真正的字典,每个词(key)有自己的定义(value)。
核心特点: 每个key都是唯一的。key用来快速查找对应的value。元素顺序通常不保证。
啥时候用? 当你需要根据某个唯一ID(key)快速查找value时。
生活例子: 通讯录(名字-电话)、商品数据库(商品ID-描述)、app设置(设置名-value)。
集合 (Sets)
啥意思? 无序的唯一元素集合。就像数学里的集合。
核心特点: 不能有重复元素。元素顺序不保证。适合判断元素是否存在、做集合运算(并集、交集)。
啥时候用? 当你只关心唯一值,想快速判断某个元素在不在集合里。
生活例子: 网站独立访客名单、文章标签集合、自动补全词库(无重复)。
队列 (Queues)
啥意思? 按“先进先出”原则工作的集合(FIFO - First-In, First-Out)。
核心特点: 元素加到队尾,从队头取出。
啥时候用? 模拟处理顺序很重要的流程,比如客服工单、打印队列。
栈 (Stacks)
啥意思? 按“后进先出”原则工作的集合(LIFO - Last-In, First-Out)。
核心特点: 元素只在一端(栈顶)加和取。
啥时候用? 追踪操作历史(比如编辑器撤销)、处理嵌套结构、递归。
集合类型的区别
| 集合类型 | 类比 | 核心原理 | 能按下标访问? | 有顺序吗? | 允许重复? | 主要操作 |
|---|---|---|---|---|---|---|
| 数组 | 一排编号格子 | 固定大小 | 有 | 有 | 有 | 按下标取/设 |
| 列表 | 购物清单 | 动态、有序 | 有 | 有 | 有 | 添加、删除、查找 |
| 字典 | 字典(key-value) | key唯一 | 没有 | 没有(通常) | 没有(按key) | 按key取、添加 |
| 集合 | 唯一对象集合 | 只存唯一元素 | 没有 | 没有 | 没有 | 判断存在、并集 |
| 队列 | 超市排队 | FIFO(先进先出) | 没有 | 有 | 有 | 末尾加、头部取 |
| 栈 | 盘子堆 | LIFO(后进先出) | 没有 | 有 | 有 | 顶部加、顶部取 |
3. 列表:List<T>
列表是C#里最常用的集合。甚至比数组还常用。它和数组很像,但能自己变大。你可以随意加、删元素,完全不用操心。
using System;
using System.Collections.Generic;
var numbers = new List<int>(); // 创建空的int列表
numbers.Add(10); // 加一个元素
numbers.Add(15);
numbers.Add(42);
Console.WriteLine(numbers[0]); // 10
numbers.Remove(15); // 按值删除元素
foreach (var number in numbers)
{
Console.WriteLine(number);
}
// 会输出:10 和 42
为啥List<T>比数组更适合存放数量不确定的数据?
—— 因为你不用手动分配更多内存、复制数组,添加元素这些事集合都帮你搞定了!
什么时候用List<T>?
- 需要动态增删改元素的列表。
- 不需要按key快速查找(那是字典的活)。
- 经常处理有序数据集。
4. 字典 Dictionary<TKey, TValue>
数组(和列表)能存一串值,每个格子有下标。而字典允许你用字符串(名字)当下标。这种下标叫key。
如果你想根据某个key快速查value(比如用借书证号查读者名字),那就得用字典。
using System.Collections.Generic;
var phoneBook = new Dictionary<string, string>();
phoneBook["安雅"] = "+79992221133";
phoneBook["马克西姆"] = "+79998887766";
Console.WriteLine(phoneBook["安雅"]); // +79992221133
// 可以判断key是否存在:
if (phoneBook.ContainsKey("瓦夏"))
{
Console.WriteLine(phoneBook["瓦夏"]);
}
else
{
Console.WriteLine("没有这个号码!");
}
有趣的小知识: 字典常被叫做“关联数组”。它们底层用哈希表实现,所以查找key超级快(几乎瞬间,除非碰撞——这个以后再说)。
核心特点
- key唯一:字典里不能有重复key。
- value可以重复。
- 按key查找、添加、删除都很快。
5. 集合:HashSet<T>
除了列表和字典,集合也很常用。它和列表差不多,但更简单:没有固定顺序。适合只关心有哪些值,不在乎顺序。
比如你只想知道某个东西在不在集合里,不要重复,也不关心顺序——那就用集合。
using System.Collections.Generic;
var knownUsers = new HashSet<string>();
knownUsers.Add("admin");
knownUsers.Add("guest");
knownUsers.Add("admin"); // 再加一次会被忽略
Console.WriteLine(knownUsers.Contains("admin")); // True
Console.WriteLine(knownUsers.Count); // 2
集合判断元素是否存在非常快。
集合有啥用?
—— 比如你想存所有一个月内打开app的独立用户,或者所有独特的书籍作者。
特点
- 只存唯一元素(重复的会被忽略)。
- 没有下标。
- 判断是否存在很快。
6. 队列和栈
还有些专用结构:队列和栈。其实就是特殊用法的列表,只是加和取的方式有规定。
Queue<T>:队列(先进先出)
有时候你需要“排队”——新元素加到末尾,取元素只能从头部。
using System.Collections.Generic;
var queue = new Queue<string>();
queue.Enqueue("第一个");
queue.Enqueue("第二个");
queue.Enqueue("第三个");
Console.WriteLine(queue.Dequeue()); // "第一个"
Console.WriteLine(queue.Peek()); // "第二个",但不删除
Stack<T>:栈(后进先出)
栈正好相反:最后进去的最先出来。常用于解析器、函数调用、编辑器撤销等场景。
using System.Collections.Generic;
var stack = new Stack<string>();
stack.Push("一");
stack.Push("二");
stack.Push("三");
Console.WriteLine(stack.Pop()); // "三"
Console.WriteLine(stack.Peek()); // "二"
7. 表格:主流集合对比
| 集合 | 唯一元素 | 能按下标访问 | 能按key快速查找 | 插入/删除操作 |
|---|---|---|---|---|
|
没有 | 有 | 没有 | 末尾很快 |
|
key | 没有 | 有 | 按key很快 |
|
有 | 没有 | 有* | 按值很快 |
|
没有 | 没有 | 没有 | 很快(FIFO) |
|
没有 | 没有 | 没有 | 很快(LIFO) |
每种集合我们都会在后面课程里详细讲。接下来要聊聊神秘的<T>...
GO TO FULL VERSION