背景:有兩個(gè)集合,A集合裝有員工id、name,B集合裝有員工id、sex,員工id相同,分別有百萬(wàn)數(shù)據(jù),怎樣在效率最高的情況將兩個(gè)集合合并成C集合id、name、sex。
一開(kāi)始想到的是兩種最簡(jiǎn)單的思路
- 兩次Foreach循環(huán)查詢(xún)匹配。
- 使用LINQ查詢(xún)。
第一種兩次foreach循環(huán)次數(shù)為n*n,第二種linq循環(huán)次數(shù)未知,那么就開(kāi)始測(cè)試耗時(shí)了。
數(shù)據(jù)準(zhǔn)備
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循環(huán)方法
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循環(huán)方法
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);
}
在測(cè)試中非常尷尬的發(fā)現(xiàn)百萬(wàn)級(jí)數(shù)據(jù)已經(jīng)跑不動(dòng)了,暫時(shí)先改為1萬(wàn)數(shù)據(jù)進(jìn)行測(cè)試,在整理代碼的同時(shí)在foreach循環(huán)方法中又增加了一點(diǎn)優(yōu)化,兩種代碼如下
foreach+lambda循環(huán)
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循環(huán)
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);
}
具體耗時(shí)如下圖:

可以發(fā)現(xiàn)linq完勝,lambda中的where 效率也是比f(wàn)oreach+if判斷的方式效率略高一點(diǎn)點(diǎn)。
經(jīng)過(guò)大佬指點(diǎn),只回復(fù)了字典二字,寫(xiě)出如下代碼
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);
}
這時(shí)將數(shù)據(jù)量恢復(fù)到百萬(wàn)級(jí)別 得到如下結(jié)果

原因是字典中每一個(gè)元素都是鍵值對(duì),字典的key就相當(dāng)于數(shù)據(jù)庫(kù)中的索引。
你知道Dictionary查找速度為什么快嗎
這篇文章講的很詳細(xì),我就不羅嗦了。
在和朋友探討時(shí),朋友又給出了一個(gè)優(yōu)化,就是將ContactListByListAndDic方法中的list更改為stack。
結(jié)果如下:數(shù)據(jù)偶爾有波動(dòng),大部分情況下stack獲勝。

其中查看list.Add源碼

其中這個(gè)方法是一個(gè)自動(dòng)擴(kuò)容的過(guò)程

而stack中沒(méi)有這一步

難道是每次的擴(kuò)容導(dǎo)致的?不過(guò)在指定了list的長(zhǎng)度之后發(fā)現(xiàn)還是stack快一點(diǎn),這一點(diǎn)沒(méi)有搞明白,希望大牛指點(diǎn)一下。