HashSet 与唯一性
跟踪不重复的值。
HashSet 与唯一性 是 CoddyKit 上的免费 C# Academy 课时。 这是第 3 节课,共 4 节。 你可以在下方免费阅读本课时的完整内容 — 然后在浏览器中使用内置代码编辑器和全天候 AI 导师进行实践。 这是 C# Academy 学习路径的一部分,你的进度在网页和 CoddyKit 应用中同步。 C# Academy 课程共包含 4 节课。
什么是 HashSet?
HashSet<T> 存储一组 unique 值,不包含重复项。添加一个已经存在的值时不会执行任何操作。
它和字典一样由哈希表支持,因此成员检查的平均时间复杂度为 O(1)。它不会保留插入顺序。
using System.Collections.Generic;
var seen = new HashSet<int>();
seen.Add(1);
seen.Add(1); // ignored, still one elementAdd 返回布尔值
如果值是新的,Add 会返回 true;如果值已经存在,则返回 false。
利用这个返回值,您可以一步检测重复项,而不需要额外调用 Contains。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var set = new HashSet<string>();
Console.WriteLine(set.Add("a")); // True
Console.WriteLine(set.Add("a")); // False
}
}快速成员检查
对 HashSet 调用 Contains 的平均时间复杂度为 O(1),而对 List 调用时为 O(n)。
当您反复询问“我见过这个吗?”时,set 是合适的工具。随着集合变大,速度差异会显著扩大。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var ids = new HashSet<int> { 10, 20, 30 };
Console.WriteLine(ids.Contains(20)); // True
Console.WriteLine(ids.Contains(99)); // False
}
}从列表中移除重复项
一个常见任务是接收一个列表,只保留不重复的值。从列表构造 HashSet 就可以在 O(n) 时间内完成这件事。
结果 set 中每个值只出现一次。如果需要得到列表,请将它包装起来:new List<int>(set)。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var nums = new List<int> { 1, 2, 2, 3, 3, 3 };
var unique = new HashSet<int>(nums);
Console.WriteLine(unique.Count); // 3
}
}并集
UnionWith 会添加另一个集合中的每个元素,并跳过重复项。结果包含两者中的全部元素。
此操作会直接修改 set。与其自己循环并为每个项目调用 Add,这样做要简洁得多。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var a = new HashSet<int> { 1, 2, 3 };
a.UnionWith(new[] { 3, 4, 5 });
Console.WriteLine(a.Count); // 5
}
}交集
IntersectWith 只保留也出现在另一个集合中的元素,并移除其余元素。
您可以用它查找共同项目,例如两篇文章共享的标签,或同时出现在两个 groups 中的 users。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var a = new HashSet<int> { 1, 2, 3, 4 };
a.IntersectWith(new[] { 2, 4, 6 });
Console.WriteLine(string.Join(",", a)); // 2,4
}
}差集
ExceptWith 会从集合中移除另一个集合包含的所有元素,只留下该集合独有的元素。
这是集合减法运算,即“A 中有但 B 中没有的项目”。它非常适合计算发生了哪些变化,或还剩哪些内容需要处理。
var a = new HashSet<int> { 1, 2, 3, 4 };
a.ExceptWith(new[] { 2, 4 });
// a now contains 1 and 3子集与超集
IsSubsetOf 和 IsSupersetOf 用于检查集合之间的包含关系,并返回一个 bool。
Overlaps 检查是否存在任何共享元素,SetEquals 则检查两个集合是否包含完全相同的元素,而不考虑顺序。
var a = new HashSet<int> { 1, 2 };
var b = new HashSet<int> { 1, 2, 3 };
bool sub = a.IsSubsetOf(b); // true
bool ov = a.Overlaps(b); // true元素的相等性
唯一性取决于元素类型的 GetHashCode 和 Equals,这与字典键完全相同。
对于自定义类型,请重写这两个方法,或使用 record;否则,两个逻辑上相等的对象会被视为不同对象,最终都会进入 set。
var names = new HashSet<string>(
System.StringComparer.OrdinalIgnoreCase);
names.Add("Sam");
bool dup = !names.Add("SAM"); // true: treated as same没有索引和顺序
HashSet 不支持索引访问;您不能写出 set[0]。您只能使用 foreach 对其进行枚举。
枚举顺序不受保证。如果既需要唯一性又需要 sorted 顺序,请使用 SortedSet<T>,但要接受操作时间复杂度为 O(log n)。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var s = new SortedSet<int> { 3, 1, 2 };
Console.WriteLine(string.Join(",", s)); // 1,2,3
}
}跟踪已见项目
一种常见模式是过滤数据流,使每个值只出现一次。请将值添加到 set 中,并且只在 Add 返回 true 时执行操作。
这比检查不断增长的列表更清晰、更快,可以将 O(n²) 的扫描转变为 O(n)。
using System;
using System.Collections.Generic;
class Program {
static void Main() {
var seen = new HashSet<int>();
foreach (int x in new[] { 1, 1, 2, 3, 2 })
if (seen.Add(x)) Console.Write(x + " ");
// prints 1 2 3
}
}快速检查
请回忆 HashSet 保证的特性及其查找成本。
回顾
HashSet<T> 存储 unique 值,添加、移除和成员检查的平均时间复杂度为 O(1),但不提供顺序或索引。
它非常适合去重、快速执行“我见过这个吗”的检查,以及通过 UnionWith、IntersectWith 和 ExceptWith 进行 set 集合运算。自定义元素类型需要实现正确的相等性。
常见问题解答
「HashSet 与唯一性」课时是免费的吗?
是的 — 「HashSet 与唯一性」的完整文本可在网页上免费阅读。要进行交互式练习(内置代码编辑器和全天候 AI 导师)并解锁 C# Academy 课程的其余内容,请升级到 CoddyKit PRO。 C# Academy 课程共包含 4 节课。
「HashSet 与唯一性」这节课中我会学到什么?
跟踪不重复的值。 你通过在浏览器中直接运行的动手代码来练习 C# Academy,全天候 AI 导师会在你学习这节课的过程中回答你的问题。
学习 C# Academy 需要有经验吗?
无需任何先前经验。CoddyKit 上的 C# Academy 课程适合初学者到高级学习者,你可以从这里开始或从头开始,按照自己的节奏学习。 这是第 3 节课,共 4 节。
「HashSet 与唯一性」课时需要多长时间?
大多数 CoddyKit 课程大约需要 5–10 分钟。每节课都很精短且互动,所以你能稳步进步,并在网页和应用中从离开的地方继续。
我能在这节 C# Academy 课中编写并运行代码吗?
能。每节 C# Academy 课都包含内置代码编辑器,你可以在浏览器中直接编写并运行真实代码,并获得即时 AI 反馈 — 无需本地设置。