选择集合
权衡取舍与性能。
选择集合 是 CoddyKit 上的免费 C# Academy 课时。 这是第 4 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
先问一个问题
选择集合要从一个问题开始:您将如何访问数据?按位置、按键,还是只检查成员是否存在?
List、Dictionary 和 HashSet 分别适用于不同的访问模式。让工具匹配访问模式,代码就能保持快速而清晰。
按位置访问:列表
如果顺序很重要,并且您通过索引访问项目,请选择 List<T>。它会保留插入顺序,并提供 O(1) 的索引访问。
例如:由 steps 组成的队列、按显示顺序排列的行,或任何从头到尾遍历的序列。它允许重复项。
var steps = new List<string> { "mix", "bake", "cool" };
string first = steps[0]; // O(1) by index按键访问:字典
如果您通过唯一标识符查找内容,请选择 Dictionary<K,V>。它将键映射到值,平均时间复杂度为 O(1)。
例如:从用户标识符映射到用户、从国家代码映射到名称,或从单词映射到其计数。键回答“是哪一个”,值承载数据。
var users = new Dictionary<int, string> {
[101] = "Ann",
[102] = "Bob"
};
string name = users[101];成员关系与唯一性:HashSet
如果您只关心某个值是否存在,或者必须拒绝重复项,请选择 HashSet<T>。Contains 的平均时间复杂度为 O(1)。
例如:visited(已访问的)网址、允许的权限和不重复的标签。这里没有附加的值,只有元素是否存在。
var visited = new HashSet<string>();
if (visited.Add(url)) {
// first time seeing this url
}成本表
平均成本如下:List 的索引访问为 O(1),但 Contains 为 O(n)。Dictionary 和 HashSet 的查找为 O(1)。
List.Add 在末尾添加的摊销时间复杂度为 O(1);在中间插入或移除则为 O(n)。Dictionary 和 HashSet 的添加与移除平均为 O(1)。
// List: index O(1), Contains O(n)
// Dictionary: by-key O(1), no index
// HashSet: Contains O(1), no value, no index列表 Contains 是一种代码异味
在循环中反复调用 list.Contains 是一个 O(n²) 陷阱。每次检查都会扫描整个列表。
如果成员检查占据了主要开销,请改用 HashSet。在大型数据上,这一个改动就能将缓慢的循环变得近乎即时。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var allow = new HashSet<int> { 2, 4, 6 };
foreach (int n in new[] { 1, 2, 3, 4 })
if (allow.Contains(n)) Console.Write(n + " ");
}
}同时需要键和顺序时
既需要按键查找,又需要可预测的顺序?标准 Dictionary 不保证顺序。
您可以让 List 负责保存顺序,同时让 Dictionary 负责查找;也可以使用 SortedDictionary<K,V>,让键保持 sorted 顺序,但操作成本为 O(log n)。
var sorted = new SortedDictionary<string, int>();
sorted["b"] = 2;
sorted["a"] = 1;
// enumerates a then b, in key order内存权衡
基于哈希的集合用内存换取速度。Dictionary 和 HashSet 会维护内部桶,因此比紧凑的 List 或数组占用更多内存。
对于只有少量项目的小型集合,扫描 List 实际上可能已经足够,而且占用的内存更少。数据规模较大时,哈希的优势才会显现。
Program 面向接口设计
方法签名应请求能够工作的最不具体类型。要读取数据时接受 IEnumerable<T>,要进行索引读取时接受 IReadOnlyList<T>,要按键访问时接受 IDictionary<K,V>。
这样可以解除调用方与具体选择之间的耦合,让您以后替换实现而不必破坏签名。
int Sum(IEnumerable<int> values) {
int total = 0;
foreach (int v in values) total += v;
return total;
}一个完整示例
统计文本中的不重复单词需要结合使用两个集合。HashSet 跟踪标记为 seen 的单词;Dictionary 记录每个单词的 counts(次数)。
两者各司其职:set 保证唯一性,字典将单词映射到频率,并且每次操作的平均时间复杂度都是 O(1)。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var counts = new Dictionary<string, int>();
foreach (var w in "a b a c b a".Split(' '))
counts[w] = counts.GetValueOrDefault(w) + 1;
Console.WriteLine(counts["a"]); // 3
}
}决策清单
请按顺序询问:我是否需要键到值的映射?请使用 Dictionary。我是否只需要唯一性或成员检查?请使用 HashSet。
否则,我是否需要顺序和索引访问,并且可能包含重复项?请使用 List。这份简短的清单涵盖了大多数日常情况。
快速检查
请将决策清单应用到一个具体需求中。
回顾
根据访问模式进行选择:有序的索引序列使用 List;键到值的查找使用 Dictionary;唯一性和成员检查使用 HashSet。
请注意大 O:避免在高频循环中使用 List.Contains,依靠 O(1) 的哈希查找,并面向接口进行编程,从而保持选择的灵活性。
常见问题解答
「选择集合」课时是免费的吗?
是的 — 「选择集合」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「选择集合」这节课中我会学到什么?
权衡取舍与性能。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 4 节课,共 4 节。
「选择集合」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。