zoukankan      html  css  js  c++  java
  • 实例演示 C# 中 Dictionary<Key, Value> 的检索速度远远大于 hobbyList.Where(c => c.UserId == user.Id)

    前言

    我们知道,有时候在一些项目中,为了性能,往往会一次性加载很多条记录来进行循环处理(备注:而非列表呈现)。比如:从数据库中加载 10000 个用户,并且每个用户包含了 20  个“爱好”,在 WinForm 界面我们需要用进度条的方式实时显示每个用户处理的进度,这时候当然是一次性加载很多条记录来进行循环处理更加快速。如下图:

    问题和解决方案

    通常,我们会先 foreach 从数据库中加载的用户集合,然后从“爱好”集合中筛选出某个用户的所有爱好来进行处理。比如如下代码:

    foreach (var userItem in userList)
    {
        List<Hobby> tempHobbyList = hobbyList.Where(c => c.UserId == userItem.Id).ToList();
        foreach (var hobbyItem in tempHobbyList)
        {
            string hobbyName = hobbyItem.HobbyName;
            //...其它的代码
        }
    }

    如下图,我们编写了如上面的代码来进行实际测试,结果发现,当 userList 足够多、或者 hobbyList 足够多,或者 userList、hobbyList 同时都足够多时,性能会大大的降低,这时候就需要我们改变方式,采用先把 hobbyList 安装 userId 进行分组(GroupBy)变成 Dictionary<int, List<Hobby>>,然后在 userList 的循环体中直接从 Dictionary<int, List<Hobby>> 从检索,这样性能会大大提高。

    实际测试

    namespace ConsAppTest
    {
        class User
        {
            public int Id { get; set; }
            public string Name { get; set; }
            public int? Age { get; set; }
        }
    
        class Hobby
        {
            public int HobbyId { get; set; }
            public string HobbyName { get; set; }
            public int UserId { get; set; }
        }
    
        class Program
        {
            private const int UserCountForNew = 6000;
            private const int HobbyCountForEveryUser = 20;
    
            private static async Task Main(string[] args)
            {
                string messageTemplate = $"产生 { UserCountForNew } 个虚拟用户,并且每个虚拟用户产生了 { HobbyCountForEveryUser } 个“爱好”共耗时 {{0}} 秒。";
    
                //下面是方案1 - 开始
                Stopwatch globalWatch1 = Stopwatch.StartNew();
                List<User> userList1;
                List<Hobby> hobbyList1;
                CreateUserListAndHobbyList(out userList1, out hobbyList1);
                globalWatch1.Stop();
                double totalSeconds1 = (double)globalWatch1.ElapsedMilliseconds / 1000;
                Console.WriteLine(messageTemplate, totalSeconds1.ToString("#.##"));
    
                Stopwatch stage1Watch = Stopwatch.StartNew();
                foreach (var userItem in userList1)
                {
                    List<Hobby> tempHobbyList = hobbyList1.Where(c => c.UserId == userItem.Id).ToList();
                    foreach (var hobbyItem in tempHobbyList)
                    {
                        string hobbyName = hobbyItem.HobbyName;
                    }
                }
                stage1Watch.Stop();
                double stage1WatchTotalSeconds = (double)stage1Watch.ElapsedMilliseconds / 1000;
                Console.WriteLine("方案1总耗时:{0}
    
    ", stage1WatchTotalSeconds.ToString("#.##"));
                //下面是方案1 - 结束
    
                //下面是方案2 - 开始
                Stopwatch globalWatch2 = Stopwatch.StartNew();
                List<User> userList2;
                List<Hobby> hobbyList2;
                CreateUserListAndHobbyList(out userList2, out hobbyList2);
                globalWatch2.Stop();
                double totalSeconds2 = (double)globalWatch2.ElapsedMilliseconds / 1000;
                Console.WriteLine(messageTemplate, totalSeconds2.ToString("#.##"));
                
                Stopwatch stage2Watch = Stopwatch.StartNew();
                foreach (var userItem in userList2)
                {
                    List<Hobby> tempHobbyList = hobbyList2.Where(c => c.UserId == userItem.Id).ToList();
                    foreach (var hobbyItem in tempHobbyList)
                    {
                        string hobbyName = hobbyItem.HobbyName;
                    }
                    hobbyList2.RemoveAll(c => c.UserId == userItem.Id);
                }
                stage2Watch.Stop();
                double stage2WatchTotalSeconds = (double)stage2Watch.ElapsedMilliseconds / 1000;
                Console.WriteLine("方案2总耗时:{0}
    
    ", stage2WatchTotalSeconds.ToString("#.##"));
                //下面是方案2 - 结束
                
    
                //下面是方案3 - 开始
                Stopwatch globalWatch3 = Stopwatch.StartNew();
                List<User> userList3;
                List<Hobby> hobbyList3;
                CreateUserListAndHobbyList(out userList3, out hobbyList3);
                globalWatch3.Stop();
                double totalSeconds3 = (double)globalWatch3.ElapsedMilliseconds / 1000;
                Console.WriteLine(messageTemplate, totalSeconds3.ToString("#.##"));
    
                Stopwatch stage3Watch = Stopwatch.StartNew();
                Dictionary<int, List<Hobby>> keyValuePairs = hobbyList3.GroupBy<Hobby, int>(c => c.UserId).ToDictionary(c => c.Key, c => c.ToList());
                foreach (var userItem in userList3)
                {
                    List<Hobby> tempHobbyList = keyValuePairs[userItem.Id];
                    foreach (var hobbyItem in tempHobbyList)
                    {
                        string hobbyName = hobbyItem.HobbyName;
                    }
                }
                stage3Watch.Stop();
                double stage3WatchTotalSeconds = (double)stage3Watch.ElapsedMilliseconds / 1000;
                Console.WriteLine("方案3总耗时:{0}
    
    ", stage3WatchTotalSeconds.ToString("#.##"));
    
                //下面是方案3 - 结束
    
                Console.ReadLine();
    
                
            }
    
            private static void CreateUserListAndHobbyList(out List<User> rightUserList, out List<Hobby> rightHobbyList)
            {
                List<User> userList = new List<User>();
                List<Hobby> hobbyList = new List<Hobby>();
                int hobbyIndex = 0;
                for (int i = 1; i <= UserCountForNew; i++)
                {
                    int userId = i;
                    userList.Add(new User { Id = userId, Name = "张三" + userId, Age = userId });
                    for (int j = 0; j < HobbyCountForEveryUser; j++)
                    {
                        hobbyIndex++;
                        hobbyList.Add(new Hobby
                        {
                            HobbyId = hobbyIndex,
                            HobbyName = "唱歌" + hobbyIndex,
                            UserId = userId
                        });
                    }
                }
                rightUserList = userList;
                rightHobbyList = hobbyList;
            }
    
    
        }
    }

    运行截图

    结论

    最终,我们看到,这三种方案,每种方案都产生 6000 个虚拟用户,并且每个虚拟用户产生了 20 个“爱好”。第一二种方案检索的速度要 8秒、9秒左右,而第三种方案从 Dictionary<int, List<Hobby>> 中检索只需要 0.01 到 0.02 秒,可谓是性能大大提升。

    谢谢浏览!

  • 相关阅读:
    兑奖
    杨辉三角
    偶数求和
    进制转化
    填词
    等值数目
    Spring框架的七个模块
    数据库中的第1、2、3范式 (昨天没睡好,因为那个蚊子~~)
    关于eclipse 不编译或者找不到*.class的问题
    servlet生命周期的理解
  • 原文地址:https://www.cnblogs.com/Music/p/fast-dictionary-for-searching-in-csharp.html
Copyright © 2011-2022 走看看