.NET 中如何快速实现 List 集合去重?
前言在数据处理中去除集合中的重复元素是一个常见的需求。.NET 6 和 .NET 7 引入了DistinctBy方法这是一个非常实用的新特性可以方便地根据指定的键对集合进行去重。本文将详细介绍DistinctBy方法的使用并通过具体的案例来展示其在实际开发中的应用。正文1、DistinctBy方法DistinctBy方法允许我们在 LINQ 查询中根据某个键对集合中的元素进行去重。这个方法返回一个新的集合其中只包含根据指定键唯一确定的元素。方法签名public static IEnumerableTSource DistinctByTSource, TKey( this IEnumerableTSource source, FuncTSource, TKey keySelector );2、基本用法最简单的用法是在 LINQ 查询中直接调用DistinctBy方法然后处理去重后的集合。说明假设我们有一个用户列表我们想要根据用户名去除重复的用户。using System.Linq; class User { public string Name { get; set; } public int Age { get; set; } } var users new ListUser { new User { Name Alice, Age 25 }, new User { Name Bob, Age 32 }, new User { Name Alice, Age 28 }, new User { Name David, Age 35 } }; var distinctUsers users.DistinctBy(user user.Name); foreach (var user in distinctUsers) { Console.WriteLine($Name: {user.Name}, Age: {user.Age}); }输出结果Name: Alice, Age: 25 Name: Bob, Age: 32 Name: David, Age: 35过滤前后元素还是保持原有的顺序我们可以查看源码。源码private static IEnumerableTSource DistinctByIteratorTSource, TKey(IEnumerableTSource source, FuncTSource, TKey keySelector, IEqualityComparerTKey? comparer) { using IEnumeratorTSource enumerator source.GetEnumerator(); if (enumerator.MoveNext()) { var set new HashSetTKey(DefaultInternalSetCapacity, comparer); do { TSource element enumerator.Current; if (set.Add(keySelector(element))) { yield return element; } } while (enumerator.MoveNext()); } }通过查看源码可以看到是利用了HashSet去重元素顺序并未被打乱。在处理集合时我们经常需要去除重复的元素同时保持原有的顺序。使用HashSet可以高效地实现这一目标。首先将指定的键尝试添加到HashSet中如果添加成功说明该键没有重复如果添加失败说明已经存在相同的键此元素将被过滤掉。3、复杂用法DistinctBy方法可以用于更复杂的去重逻辑例如根据多个属性进行去重。说明假设我们有一个订单列表我们想要根据客户名称和订单金额去除重复的订单。class Order { public int OrderId { get; set; } public string CustomerName { get; set; } public decimal Amount { get; set; } } var orders new ListOrder { new Order { OrderId 1, CustomerName Alice, Amount 100.0m }, new Order { OrderId 2, CustomerName Bob, Amount 150.0m }, new Order { OrderId 3, CustomerName Alice, Amount 100.0m }, new Order { OrderId 4, CustomerName Charlie, Amount 120.0m }, new Order { OrderId 5, CustomerName Bob, Amount 150.0m } }; var distinctOrders orders.DistinctBy(order (order.CustomerName, order.Amount)); foreach (var order in distinctOrders) { Console.WriteLine($Order ID: {order.OrderId}, Customer: {order.CustomerName}, Amount: {order.Amount}); }输出结果Order ID: 1, Customer: Alice, Amount: 100.0 Order ID: 2, Customer: Bob, Amount: 150.0 Order ID: 4, Customer: Charlie, Amount: 120.04、性能考虑DistinctBy方法在内部使用哈希表来跟踪已经出现的键因此在大多数情况下性能非常好。但在处理非常大的数据集时仍然需要注意内存使用情况。说明假设我们有一个包含数百万条记录的大集合我们需要根据某个键进行去重。var largeCollection Enumerable.Range(1, 10000000).Select(i new { Id i, Value i % 1000 }); var distinctLargeCollection largeCollection.DistinctBy(item item.Value); Console.WriteLine($Distinct count: {distinctLargeCollection.Count()});5、异步 LINQ 查询中的使用DistinctBy方法也可以在异步 LINQ 查询中使用结合IAsyncEnumerableT类型处理大量数据时更加高效。说明假设我们有一个异步方法返回一个用户列表我们想要根据用户名去除重复的用户。using System.Net.Http.Json; public async IAsyncEnumerableUser GetUsersAsync() { var response await httpClient.GetAsync(https://api.example.com/users); var usersJson await response.Content.ReadAsStringAsync(); // 使用Json序列化工具解析用户列表 var users JsonSerializer.DeserializeListUser(usersJson); foreach (var user in users) { yield return user; } } // 使用异步LINQ查询 var distinctUsers await GetUsersAsync().DistinctByAsync(user user.Name).ToListAsync(); foreach (var user in distinctUsers) { Console.WriteLine($Name: {user.Name}, Age: {user.Age}); }总结DistinctBy方法是 .NET 6 和 .NET 7 中 LINQ 的一个非常实用的新特性。我们在 LINQ 查询中根据指定的键对集合进行去重简化了代码并提高了开发效率。希望本文能帮助大家更好地理解和利用 .NET 6 和 .NET 7 中 LINQ 的DistinctBy方法从而在项目中发挥更大的作用。