.NET 中高效实现 List 集合去重的多种方法详解

.NET 中高效实现 List 集合去重的多种方法详解

在 .NET 开发中,处理集合数据时经常会遇到需要对 List<T> 进行去重的场景。无论是基本类型(如 int、string)还是自定义对象,.NET 提供了多种简洁高效的去重方式。本文将系统介绍几种常用且性能良好的去重方法,并对比其适用场景。


一、使用 LINQ 的 Distinct() 方法(最常用)

LINQ 提供了最简洁的去重方式:Distinct() 扩展方法。

1. 基本类型去重

复制代码
var numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
var uniqueNumbers = numbers.Distinct().ToList();
// 结果: [1, 2, 3, 4, 5]

2. 自定义对象去重(需实现 IEqualityComparer 或重写 Equals/GetHashCode)

若直接对自定义类调用 Distinct(),默认比较的是引用(除非重写了 Equals 和 GetHashCode)。

方式 A:重写 Equals 和 GetHashCode
复制代码
public class Person
{
    public string Name { get; set; }
    public int Age { get; set; }

    public override bool Equals(object obj) => obj is Person p && Name == p.Name && Age == p.Age;
    public override int GetHashCode() => HashCode.Combine(Name, Age);
}

var people = new List<Person>
{
    new Person { Name = "Alice", Age = 30 },
    new Person { Name = "Alice", Age = 30 },
    new Person { Name = "Bob", Age = 25 }
};

var uniquePeople = people.Distinct().ToList(); // 自动去重
方式 B:使用自定义 IEqualityComparer
复制代码
public class PersonNameComparer : IEqualityComparer<Person>
{
    public bool Equals(Person x, Person y) => x?.Name == y?.Name;
    public int GetHashCode(Person obj) => obj?.Name?.GetHashCode() ?? 0;
}

var uniqueByName = people.Distinct(new PersonNameComparer()).ToList();

二、使用 HashSet(高性能推荐)

HashSet<T> 天然具有去重特性,插入时自动忽略重复项,性能优于 Distinct()(尤其在大数据量时)。

复制代码
var numbers = new List<int> { 1, 2, 2, 3, 4, 4, 5 };
var uniqueNumbers = new HashSet<int>(numbers).ToList();

对于自定义对象,同样需要提供合适的 IEqualityComparer<T>:

复制代码
var uniquePeople = new HashSet<Person>(people, new PersonNameComparer()).ToList();

✅ 优点 :时间复杂度接近 O(n),适合频繁去重或大数据集。

❌ 注意 :HashSet 不保证元素顺序(但 .NET Core 6+ 的 HashSet 在某些情况下保持插入顺序,不建议依赖此行为)。


三、使用 ToLookup / GroupBy(按条件去重)

如果需要"保留第一个出现的元素"并按特定字段去重,可结合 GroupBy:

复制代码
var uniquePeople = people
    .GroupBy(p => p.Name)
    .Select(g => g.First())
    .ToList();

这种方式灵活,适用于复杂去重逻辑(如保留最新记录、按多个字段分组等)。


四、.NET 6+ 新特性:DistinctBy()

从 .NET 6 开始,LINQ 新增了 DistinctBy() 方法,无需实现比较器即可按属性去重:

复制代码
var uniquePeople = people.DistinctBy(p => p.Name).ToList();
// 或按多个属性
var uniqueByBoth = people.DistinctBy(p => new { p.Name, p.Age }).ToList();

✅ 强烈推荐:代码简洁、语义清晰、性能良好,是现代 .NET 项目的首选方案。


五、性能与选型建议

方法 适用场景 是否保序 性能 .NET 版本要求
Distinct() 简单去重,已重写 Equals/GetHashCode 是 中等 所有版本
HashSet<T> 大数据量、高频去重 否(通常) 高 所有版本
GroupBy + First 按条件去重、需控制保留逻辑 是 中等 所有版本
DistinctBy() 按属性去重、代码简洁 是 高 .NET 6+

总结

  • 对于 基本类型 ,直接使用 Distinct() 即可。
  • 对于 自定义对象 :
    • 若使用 .NET 6 及以上 ,优先使用 DistinctBy();
    • 若需兼容旧版本,可选择 GroupBy 或实现 IEqualityComparer<T>;
    • 若追求极致性能且不关心顺序,使用 HashSet<T>。

合理选择去重方式,不仅能提升代码可读性,还能显著优化程序性能。希望本文能帮助你在 .NET 项目中高效处理集合去重问题!

相关推荐
玩泥巴的14 小时前
在飞书 SDK 上用 Microsoft Agent Framework 搭一个能干活的工作搭子
ai·.net·飞书·agent
波力海苔夹心脆67521 小时前
C# 委托与事件详解:委托声明、Action/Func、多播、event 与 EventHandler 入门教程
开发语言·c#·.net·上位机·委托·事件
IT规划师1 天前
MQTT实战 - MQTT 5.0新特性实战:共享订阅、Topic Alias、流控(十)
物联网·.net
玩泥巴的2 天前
一份声明,多份产物:飞书 SDK 的 AI 工具面是怎么生成的
ai·.net·飞书·agent
IT规划师2 天前
MQTT实战 - .NET + MQTT生产级架构:从500个设备到50,000个设备(九)
物联网·.net
mudtools3 天前
一份声明,多份产物:飞书 SDK 的 AI 工具面是怎么生成的
人工智能·.net·ai编程
云中飞鸿3 天前
.Net和C#有什么关系?(从C++视角观察)
开发语言·c#·.net
2401_885885044 天前
国际语音C#接口怎么写?.NET环境下实现国际语音验证码批量下发
开发语言·c#·.net
1-1=04 天前
.NET Core 本地化 (L10n)
.net·.netcore