美文网首页
c# 两个不同类海量数据集合的合并

c# 两个不同类海量数据集合的合并

作者: 七逃叔 | 来源:发表于2019-01-31 18:21 被阅读0次

    背景:有两个集合,A集合装有员工id、name,B集合装有员工id、sex,员工id相同,分别有百万数据,怎样在效率最高的情况将两个集合合并成C集合id、name、sex。

    一开始想到的是两种最简单的思路

    1. 两次Foreach循环查询匹配。
    2. 使用LINQ查询。

    第一种两次foreach循环次数为n*n,第二种linq循环次数未知,那么就开始测试耗时了。
    数据准备

        class Program
        {
            public static List<Amodel> AList { get; set; } = new List<Amodel>();
            public static List<Bmodel> BList { get; set; } = new List<Bmodel>();
    
            static void Main(string[] args)
            {
                for (int i = 0; i < 1000*1000 ; i++)
                {
                    AList.Add(new Amodel() { id = i, name = Guid.NewGuid().ToString() });
                    BList.Add(new Bmodel() { id = i, sex = Guid.NewGuid().ToString() });
                }
                Console.ReadKey();
            }
        }
    
        public class model
        {
            public int id { get; set; }
            public string name { get; set; }
    
            public string sex { get; set; }
        }
    
        public class Amodel
        {
            public int id { get; set; }
            public string name { get; set; }
        }
    
        public class Bmodel
        {
            public int id { get; set; }
            public string sex { get; set; }
        }
    

    foreach循环方法

    static void ContactListByForeach()
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                List<model> resultList = new List<model>();
                foreach (var itemA in AList)
                {
                    foreach (var itemB in BList)
                    {
                        if (itemA.id == itemB.id)
                        {
                            var model = new model
                            {
                                id = itemA.id,
                                name = itemA.name,
                                sex = itemB.sex
                            };
                            resultList.Add(model);
                        }
                    }
                }
                watch.Stop();
                Console.WriteLine("ContactListByForeach:_____________________" + watch.ElapsedMilliseconds);
            }
    

    linq循环方法

            static void ContactListByLinq()
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                var result = from a in AList
                             join b in BList on a.id equals b.id
                             select new model { id = a.id, name = a.name, sex = b.sex };
                var s = result.ToArray();
                watch.Stop();
                Console.WriteLine("ContactListByLinq:_____________________" + watch.ElapsedMilliseconds);
            }
    

    在测试中非常尴尬的发现百万级数据已经跑不动了,暂时先改为1万数据进行测试,在整理代码的同时在foreach循环方法中又增加了一点优化,两种代码如下
    foreach+lambda循环

            static void ContactListByForeachAndLambda()
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                List<model> resultList = new List<model>();
                foreach (var itemA in AList)
                {
                    var bModel=BList.Where(m=>m.id==itemA.id).FirstOrDefault();
                    var model = new model
                    {
                        id = itemA.id,
                        name = itemA.name,
                        sex = bModel.sex
                    };
                    resultList.Add(model);
                }
                watch.Stop();
                Console.WriteLine("ContactListByForeachAndLambda:_____________________" + watch.ElapsedMilliseconds);
            }
    

    纯lambda循环

             static void ContactListByForeachLambda()
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                List<model> resultList = new List<model>();
                resultList = AList.Select(m => new model {
                    id = m.id,
                    name = m.name,
                    sex = BList.Where(n => n.id == m.id).FirstOrDefault().sex
                }).ToList();
                watch.Stop();
                Console.WriteLine("ContactListByForeachLambda:_____________________" + watch.ElapsedMilliseconds);
            }
    

    具体耗时如下图:


    image.png

    可以发现linq完胜,lambda中的where 效率也是比foreach+if判断的方式效率略高一点点。
    经过大佬指点,只回复了字典二字,写出如下代码
    dic

            static void ContactListByListAndDic()
            {
                Stopwatch watch = new Stopwatch();
                watch.Start();
                var newBlist = BList.ToDictionary(key => key.id);
                List<model> tempStack = new List<model>();
                for (int i = 0; i < AList.Count; i++)
                {
                    var key = AList[i].id;
                    tempStack.Add(new model
                    {
                        id = key,
                        name = AList[i].name,
                        sex = newBlist.ContainsKey(key) ? newBlist[key].sex : string.Empty
                    });
                }
                var s = tempStack.ToArray();
                watch.Stop();
                Console.WriteLine("ContactListByListAndDic:_____________________" + watch.ElapsedMilliseconds);
            }
    

    这时将数据量恢复到百万级别 得到如下结果


    image.png

    原因是字典中每一个元素都是键值对,字典的key就相当于数据库中的索引。
    你知道Dictionary查找速度为什么快吗
    这篇文章讲的很详细,我就不罗嗦了。

    在和朋友探讨时,朋友又给出了一个优化,就是将ContactListByListAndDic方法中的list更改为stack。
    结果如下:数据偶尔有波动,大部分情况下stack获胜。


    image.png

    其中查看list.Add源码


    image.png
    其中这个方法是一个自动扩容的过程 image.png

    而stack中没有这一步


    image.png

    难道是每次的扩容导致的?不过在指定了list的长度之后发现还是stack快一点,这一点没有搞明白,希望大牛指点一下。

    相关文章

      网友评论

          本文标题:c# 两个不同类海量数据集合的合并

          本文链接:https://www.haomeiwen.com/subject/zmtlsqtx.html