.NET 中如何快速实现 List 集合去重?
在数据处理中,去除集合中的重复元素是一个常见的需求。.NET 6 和 .NET 7 引入了 DistinctBy 方法,这是一个非常实用的新特性,可以方便地根据指定的键对集合进行去重。
本文将详细介绍 DistinctBy 方法的使用,并通过具体的案例来展示其在实际开发中的应用。
正文
1、DistinctBy 方法
DistinctBy 方法允许我们在 LINQ 查询中根据某个键对集合中的元素进行去重。
这个方法返回一个新的集合,其中只包含根据指定键唯一确定的元素。
public static IEnumerable<TSource> DistinctBy<TSource, TKey>( this IEnumerable<TSource> source, Func<TSource, TKey> keySelector );
2、基本用法
最简单的用法是在 LINQ 查询中直接调用 DistinctBy 方法,然后处理去重后的集合。
说明
假设我们有一个用户列表,我们想要根据用户名去除重复的用户。
using System.Linq; class User { public string Name { get; set; } public int Age { get; set; } } var users = new List<User> { 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 IEnumerable<TSource> DistinctByIterator<TSource, TKey>(IEnumerable<TSource> source, Func<TSource, TKey> keySelector, IEqualityComparer<TKey>? comparer) { using IEnumerator<TSource> enumerator = source.GetEnumerator(); if (enumerator.MoveNext()) { var set = new HashSet<TKey>(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 List<Order> { 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.0
4、性能考虑
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 查询中使用,结合 IAsyncEnumerable<T> 类型,处理大量数据时更加高效。
说明
假设我们有一个异步方法返回一个用户列表,我们想要根据用户名去除重复的用户。
using System.Net.Http.Json; public async IAsyncEnumerable<User> GetUsersAsync() { var response = await httpClient.GetAsync("https://api.example.com/users"); var usersJson = await response.Content.ReadAsStringAsync(); // 使用Json序列化工具解析用户列表 var users = JsonSerializer.Deserialize<List<User>>(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
最后


浙公网安备 33010602011771号