HashSet用new HashSet()初始化即可,内部哈希表实现O(1)增查;存自定义类须重写GetHashCode和Equals或传IEqualityComparer;不保证顺序,多线程需手动同步。
直接用 new HashSet 创建即可,不需要手动去重逻辑。它内部基于哈希表实现,插入、查找平均时间复杂度是 O(1)。
常见错误:用 List + Contains 手动判断再添加,性能差且易漏判(尤其并发场景)。
Add() 返回 false,原集合不变HashSet、HashSet,不支持裸 HashSet
GetHashCode() 和 Equals(),否则默认引用比较,相同内容对象仍被当作不同元素默认情况下,HashSet 把两个字段完全相同的 Person 实例视为不同元素——因为没指定比较逻辑。
解决方法是传入实现了 IEqualityComparer 的比较器,或让类型自身实现该接口。
new HashSet(new PersonComparer())
Person 类实现 IEquatable 并重写 GetHashCode() 和 Equals(Person)
Equals(object) 不够,HashSet 优先调用泛型 Equals(T)
不是所有“去重需求”都该用 Hash。它无序、不可索引、不保证插入顺序(.NET 5+ 保持插入顺序,但属实现细节,不应依赖)。
典型误用场景:先去重再按原顺序遍历,却选了 HashSet 导致顺序错乱。
List.Distinct() (配合 IEqualityComparer),或手写带字典缓存的去重循环HashSet 是最优解List + HashSet 双结构维护(空间换时间)HashSet 本身不是线程安全的。多线程同时调用 Add 或 Contains 可能导致异常或数据损坏。
lock 包裹操作块,注意锁粒度别太大ConcurrentHashSet(需 NuGet 安装 System.Collections.Concurrent 扩展包),或 ConcurrentDictionary 模拟(key 存元素,value 固定为 null)foreach 遍历时修改集合(如边遍历边 Remove)会抛出 InvalidOperationException
HashSet,也没有 ConcurrentHashSet 类型(除非你装了第三方扩展包)。很多人以为 ConcurrentDictionary 有对应集合,其实没有——得自己封装或接受折中方案。**