Distinct() 去重依赖 Equals()/GetHashCode(),自定义类需重写或传 IEqualityComparer;返回新 IEnumerable,须 ToList() 赋值才更新原变量;大数据量时注意哈希冲突、装箱及内存分配。

用 Distinct() 去重前,先确认元素是否可比较
直接调用 Distinct() 很快,但结果可能和你想的不一样——它依赖元素的 Equals() 和 GetHashCode() 行为。内置类型(如 int、string)没问题;自定义类默认按引用比较,两个内容相同但不同实例的对象不会被去重。
- 如果对象是简单数据载体,给类加上
[EqualityComparer]或重写Equals()/GetHashCode() - 不想改类?传入自定义
IEqualityComparer<t></t>实现,比如按Id字段判断是否重复 - 临时用法:用
DistinctBy(x => x.Id)(需 .NET 6+ 或引入MoreLinq),比手写比较器更轻量
Distinct() 返回的是新集合,原 List<t></t> 不变
这是最常被忽略的一点:调用 Distinct() 后不重新赋值,原列表还是老样子。它返回 IEnumerable<t></t>,不是 List<t></t>,也不能直接当列表用。
- 要更新原变量:
list = list.Distinct().ToList(); - 要保持原变量类型且避免多次枚举:显式转成
List<t></t>,别留着IEnumerable<t></t> - 如果后续还要增删元素,必须用
.ToList(),否则调用Add()会报错:“不可修改的集合”
大数据量时,Distinct() 的性能瓶颈在哪
底层用哈希表实现,平均 O(n),但触发哈希冲突或大量装箱(比如对 struct 用泛型约束不严)时,实际耗时会明显上升。尤其在循环内反复调用,或对未预估大小的集合调用时容易卡住。
- 已知大概长度?初始化
new List<t>(estimatedCount)</t>再.AddRange(),比链式调用快 - 去重字段固定且简单(如全是
string)?考虑先Select(x => x.Name).Distinct().ToArray(),减少对象拷贝 - 千万级数据?别只靠
Distinct(),该用HashSet<t></t>手动遍历去重,可控内存分配
用 GroupBy() 去重时,为什么有时结果不对
list.GroupBy(x => x).Select(g => g.First()) 看似等价,但行为有隐含差异:它依赖分组键的稳定性,且当 x 是引用类型时,分组依据仍是引用地址,不是值语义。
- 如果想按某几个属性分组去重(比如“保留每个
UserId下最新的一条记录”),必须写成GroupBy(x => x.UserId).Select(g => g.OrderByDescending(y => y.CreatedAt).First()) - 注意
First()和FirstOrDefault()的区别:空分组会抛异常,生产环境建议统一用后者 - 这种写法比
Distinct()多一次遍历,数据量大时不推荐仅为了去重而用GroupBy
Distinct() + 正确的比较逻辑就能解决。难点不在语法,而在你是否清楚「相等」到底由谁定义、以及「去重后我要拿这个结果做什么」——这两点没理清,后面所有优化都是白忙。


















