背景:有两个集合,A集合装有员工id、name,B集合装有员工id、sex,员工id相同,分别有百万数据,怎样在效率最高的情况将两个集合合并成C集合id、name、sex。
一开始想到的是两种最简单的思路
- 两次Foreach循环查询匹配。
- 使用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);
}
具体耗时如下图:
可以发现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);
}
这时将数据量恢复到百万级别 得到如下结果
原因是字典中每一个元素都是键值对,字典的key就相当于数据库中的索引。
你知道Dictionary查找速度为什么快吗
这篇文章讲的很详细,我就不罗嗦了。
在和朋友探讨时,朋友又给出了一个优化,就是将ContactListByListAndDic方法中的list更改为stack。
结果如下:数据偶尔有波动,大部分情况下stack获胜。
其中查看list.Add源码
其中这个方法是一个自动扩容的过程
而stack中没有这一步
难道是每次的扩容导致的?不过在指定了list的长度之后发现还是stack快一点,这一点没有搞明白,希望大牛指点一下。