GroupBy是最自然的定位重复项方法,它将相同值元素分组后筛选出数量大于1的组;对引用类型需实现IEqualityComparer或重写Equals/GetHashCode,值类型和记录类型默认支持;可选提取重复值、首次出现的重复项或所有重复原始项,并注意null、NaN及性能优化。
用 GroupBy 找出重复元素本身
直接查哪些元素重复了,不是去重,而是定位重复项——
是最自然的选择。它把相同值的元素归成一组,再用
筛出组内数量大于 1 的那些组。
注意:如果
里是引用类型(比如自定义类),默认按引用分组,必须实现
或重写
+
,否则同内容不同实例不会被归为一组。
示例(针对
):
是重复的值,不是原始元素对象
如果要保留所有重复出现的原始项(含多次出现的全部副本),改用
在大数据量时有性能开销,可换成
+
判断是否至少有两个
用 GroupBy + First/FirstOrDefault 提取“首次出现的重复项”
有时候你不需要所有重复值,只要每个重复值第一次在原列表中出现的那个元素(比如带索引或附加属性的对象)。
这时候不能只靠
,得从组里捞出原始项:
C知道
CSDN推出的一款AI技术问答工具
下载
拿的是该值在原列表中**最早出现的那个元素**,顺序由原
保证
如果元素是匿名类或记录(
),默认已实现值相等语义,无需额外配置
对字符串要注意:默认区分大小写;如需忽略大小写,写成
Where 配合 Any 实现“过滤掉重复项”(即去重后保留唯一项)
这不是传统意义的“去重”,而是反向逻辑:只留下那些在整个列表中**仅出现一次**的元素。
常见误区是写成
—— 这是 O(n²),小数据还行,上千项就明显卡顿。
更高效的做法是先用
统计频次,再用
或字典缓存映射
极简写法(稍牺牲可读性但实用):
必须确保
一定在
中,否则会抛
;若不确定,改用
这个结果和
完全不同:
返回每个值一个代表,而这里是返回所有“没重复过”的原始项
别忘了 null 和 NaN 的特殊行为
当列表含
(引用类型)或
时,
仍能正常工作,因为
内部用的是
,它对
和
有明确定义(
为 true,
也为 true)。
但如果你手动写
做判断,
是 false,就会漏掉
重复项。
安全做法始终依赖
自带的相等比较器
对可空值类型(如
),
会被统一归为一组,无需额外处理
自定义比较器中若未显式处理
,可能引发
实际用的时候,最常踩的坑不是语法,而是没想清楚你要的是“重复的值”“重复的原始项”还是“不重复的原始项”——这三个需求对应完全不同的链式调用结构,差一个
或
就跑偏了。
GroupByWhereListIEqualityComparerEqualsGetHashCodeintvar duplicates = list.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.Key)
.ToList();g.KeySelectMany(g => g)Count()Any()Skip(1).Any()g.Keyvar firstOccurrenceOfDuplicates = list.GroupBy(x => x)
.Where(g => g.Count() > 1)
.Select(g => g.First())
.ToList();g.First()ListrecordGroupBy(x => x, StringComparer.OrdinalIgnoreCase)list.Where(x => list.Count(y => y == x) == 1)GroupByJoinvar frequencyMap = list.GroupBy(x => x).ToDictionary(g => g.Key, g => g.Count());
var uniqueItems = list.Where(x => frequencyMap[x] == 1).ToList();xfrequencyMapKeyNotFoundExceptionfrequencyMap.TryGetValue(x, out int count) && count == 1list.Distinct()Distinct()nulldouble.NaNGroupBy(x => x)GroupByEqualityComparer.DefaultnullNaNnull == nulldouble.NaN.Equals(double.NaN)Where(x => x == someValue)NaN == NaNNaNGroupByint?nullnullNullReferenceExceptionSelectSelectMany