• .NET 中如何快速实现 List 集合去重?


    前言

    在数据处理中,去除集合中的重复元素是一个常见的需求。.NET 6 和 .NET 7 引入了 DistinctBy 方法,这是一个非常实用的新特性,可以方便地根据指定的键对集合进行去重。

    本文将详细介绍 DistinctBy 方法的使用,并通过具体的案例来展示其在实际开发中的应用。

    正文

    1、DistinctBy 方法

    DistinctBy 方法允许我们在 LINQ 查询中根据某个键对集合中的元素进行去重。

    这个方法返回一个新的集合,其中只包含根据指定键唯一确定的元素。

    方法签名

    public static IEnumerable DistinctBy(
        this IEnumerable source,
        Func keySelector
    );

    2、基本用法

    最简单的用法是在 LINQ 查询中直接调用 DistinctBy 方法,然后处理去重后的集合。

    说明

    假设我们有一个用户列表,我们想要根据用户名去除重复的用户。

    复制代码
    using System.Linq;
    ​
    class User
    {
        public string Name { get; set; }
        public int Age { get; set; }
    }
    ​
    var users = new List
    {
        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 DistinctByIterator(IEnumerable source, Func keySelector, IEqualityComparer? comparer)
    {
        using IEnumerator enumerator = source.GetEnumerator();
    ​
        if (enumerator.MoveNext())
        {
            var set = new HashSet(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
    {
        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 类型,处理大量数据时更加高效。

    说明

    假设我们有一个异步方法返回一个用户列表,我们想要根据用户名去除重复的用户。

    复制代码
    using System.Net.Http.Json;
    ​
    public async IAsyncEnumerable GetUsersAsync()
    {
        var response = await httpClient.GetAsync("https://api.example.com/users");
        var usersJson = await response.Content.ReadAsStringAsync();
        
        // 使用Json序列化工具解析用户列表
        var users = JsonSerializer.Deserialize>(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 方法,从而在项目中发挥更大的作用。

    最后

    如果你觉得这篇文章对你有帮助,不妨点个赞支持一下!你的支持是我继续分享知识的动力。如果有任何疑问或需要进一步的帮助,欢迎随时留言。也可以加入微信公众号 [DotNet技术匠] 社区,与其他热爱技术的同行一起交流心得,共同成长!

  • 相关阅读:
    安装vc++后通过命令行编译qt程序
    Java集合常见面试题汇总
    【Go并发】自旋锁及Go实现
    数据结构(一)——线性链表的原理以及应用
    14.8 Socket 一收一发通信
    C++代码转C#代码,制作DLL,以供python调用
    qmt量化交易策略小白学习笔记第43期【qmt编程之期货数据--如何获取历史主力合约--原生python】
    亚马逊,富士康,腾讯等大厂的3亿美元打水漂,Android之父公司宣布关闭
    Cppcheck 详细介绍
    国外Twilio 发送sms短信
  • 原文地址:https://www.cnblogs.com/1312mn/p/18552496