HashSet<T> 底层基于哈希表实现,去重依赖 `GetHashCode()` 和 `Equals()` 两个方法。添加元素时,先计算哈希码定位桶位置,若哈希冲突则调用 `Equals()` 比较内容。若两者均判定相等,则视为重复元素不予添加。
关键机制:
1. 哈希分流:通过 `GetHashCode()` 快速定位存储桶,平均时间复杂度 O(1)。
2. 精确比对:哈希值相同(冲突)时,才执行 `Equals()` 进行逐字段比较。
3. 契约约束:若 `Equals()` 返回 true,`GetHashCode()` 必须返回相同值;反之不成立。
常见陷阱与修复:
* 引用类型默认行为:未重写上述两方法时,默认比较内存地址,导致内容相同的不同对象被视为不重复。
* 性能优化:`Add()` 返回 bool 值,直接利用返回值判断是否为新元素,避免额外调用 `Contains()` 造成二次哈希查找。
using System;
using System.Collections.Generic;
// 方案1:使用 record (C9+),编译器自动生成正确的 GetHashCode 和 Equals
public record Person(string Name, int Age);
// 方案2:传统 class 需手动重写
public class Product
{
public string Id { get; set; }
public string Name { get; set; }
public override bool Equals(object obj)
{
if (obj is Product other)
return Id == other.Id; // 根据业务主键判断
return false;
}
public override int GetHashCode()
{
return HashCode.Combine(Id); // 确保参与 Equals 的字段也参与哈希计算
}
}
class Program
{
static void Main()
{
// 1. 基础类型去重 (int/string 已内置正确实现)
var numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
var uniqueNumbers = new HashSet<int>(numbers); // 构造函数直接去重
Console.WriteLine($"Unique Numbers: {string.Join(", ", uniqueNumbers)}");
// 2. 复杂对象去重 (使用 record)
var people = new List<Person>
{
new Person("Alice", 25),
new Person("Bob", 30),
new Person("Alice", 25) // 重复
};
var uniquePeople = new HashSet<Person>(people);
// 3. 流式处理去重 (利用 Add 返回值,避免二次查找)
var rawIds = new List<string> { "A01", "A02", "A01", "A03" };
var seenIds = new HashSet<string>();
foreach (var id in rawIds)
{
if (seenIds.Add(id)) // Add 返回 true 表示是新元素,false 表示已存在
{
Console.WriteLine($"Processing new ID: {id}");
}
}
}
}
```

977

被折叠的 条评论
为什么被折叠?



