C# 哈希集合HashSet使用方法 C#如何存储不重复的元素

来源:这里教程网 时间:2026-02-21 17:39:59 作者:

HashSet 初始化和基本添加操作

直接用

new HashSet<t>()</t>
创建即可,不需要手动去重逻辑。它内部基于哈希表实现,插入、查找平均时间复杂度是 O(1)。

常见错误:用

List<t></t>
+
Contains
手动判断再添加,性能差且易漏判(尤其并发场景)。

添加重复元素时,
Add()
返回
false
,原集合不变
支持泛型约束,比如
HashSet<string></string>
HashSet<int></int>
,不支持裸
HashSet
若存自定义类,必须重写
GetHashCode()
Equals()
,否则默认引用比较,相同内容对象仍被当作不同元素

如何正确处理自定义类型去重

默认情况下,

HashSet<person></person>
把两个字段完全相同的
Person
实例视为不同元素——因为没指定比较逻辑。

解决方法是传入实现了

IEqualityComparer<t></t>
的比较器,或让类型自身实现该接口。

推荐方式:在构造时传入比较器,如
new HashSet<person>(new PersonComparer())</person>
更简洁做法:让
Person
类实现
IEquatable<person></person>
并重写
GetHashCode()
Equals(Person)
注意:仅重写
Equals(object)
不够,
HashSet
优先调用泛型
Equals(T)

HashSet 与 List/Distinct() 的实际取舍

不是所有“去重需求”都该用

HashSet
。它无序、不可索引、不保证插入顺序(.NET 5+ 保持插入顺序,但属实现细节,不应依赖)。

典型误用场景:先去重再按原顺序遍历,却选了

HashSet
导致顺序错乱。

需要保留首次出现顺序 → 用
List<t>.Distinct()</t>
(配合
IEqualityComparer
),或手写带字典缓存的去重循环
高频查询/插入且不要求顺序 →
HashSet
是最优解
要同时支持快速查重 + 按索引访问 → 考虑
List<t></t>
+
HashSet<t></t>
双结构维护(空间换时间)

线程安全与常见陷阱

HashSet<t></t>
本身不是线程安全的。多线程同时调用
Add
Contains
可能导致异常或数据损坏。

简单场景:用
lock
包裹操作块,注意锁粒度别太大
高并发场景:改用
ConcurrentHashSet<t></t>
(需 NuGet 安装
System.Collections.Concurrent
扩展包),或
ConcurrentDictionary<t object></t>
模拟(key 存元素,value 固定为
null
陷阱:用
foreach
遍历时修改集合(如边遍历边
Remove
)会抛出
InvalidOperationException
真正容易被忽略的是:**.NET 中没有内置的线程安全版
HashSet
,也没有
ConcurrentHashSet
类型(除非你装了第三方扩展包)。很多人以为
ConcurrentDictionary
有对应集合,其实没有——得自己封装或接受折中方案。**

相关推荐