项目经验总结(三)哪种方式查询泛型集合性能上最佳
这篇文章我来分析下对于泛型集合,采取不同的查询方式在性能上会有什么样的影响?
这里有一个城市简单信息的实体类:
{
public int CityID
{
get;
set;
}
public string CityName
{
get;
set;
}
public string CityNameEn
{
get;
set;
}
public string CityAddress
{
get;
set;
}
}
我们构造一个特别大的城市泛型类:
Random m = new Random();
List<CityInfo> list = new List<CityInfo>();
for (int j = 0; j < i; j++)
{
CityInfo info = new CityInfo();
info.CityID = j;
info.CityAddress = "aaaaaa" + j.ToString();
info.CityName = "城市中文名称" + j.ToString();
info.CityNameEn = "城市英文名称" + j.ToString();
list.Add(info);
}
根据城市ID查询某个城市的具体信息:
方法一:foreach:
{
CityInfo info = new CityInfo();
foreach (var item in list)
{
if (item.CityID == CityID)
{
info = item;
}
}
return info;
}
方法二:for循环:
{
CityInfo info = new CityInfo();
for (int i = 0; i < list.Count;i ++ )
{
if (list[i].CityID == CityID)
{
info = list [i];
}
}
return info;
}
方法三:Linq查询:
{
CityInfo info = new CityInfo();
info = list.Where(p => p.CityID == CityID).FirstOrDefault();
return info;
}
然后随机产生一个城市ID,分别针对上面三种方式调用500次,这里何用老赵的CodeTimer来显示信息,执行结果如下:
CodeTimer.Time("GetCityInfoByFor", 500, () => GetCityInfoByFor(list, m.Next(i - 1)));
CodeTimer.Time("GetCityInfoByLinq", 500, () => GetCityInfoByLinq(list, m.Next(i - 1)));
性能从高到低表现为:for,foreach,linq,仔细查看三种方法生成IL代码,有一定的区别:
1:foreach方法在查询数据时,依赖了Enumerator ,它的特点是不能像对于数组一样使用索引,而只能将当前项指针移动到集合的第一个或下一个元素,这是它性能不是最优的主要问题所在。在这种方式中还有一个重要点就是在查询每个元素时都会有try finally块,这也是需要消耗部分性能的。
{
IL_000d: br.s IL_0022
IL_000f: ldloca.s CS$5$0000
IL_0011: call instance !0 valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class ConsoleApplication1.CityInfo>::get_Current()
IL_0016: stloc.1
IL_0017: ldloc.1
IL_0018: callvirt instance int32 ConsoleApplication1.CityInfo::get_CityID()
IL_001d: ldarg.1
IL_001e: bne.un.s IL_0022
IL_0020: ldloc.1
IL_0021: stloc.0
IL_0022: ldloca.s CS$5$0000
IL_0024: call instance bool valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class ConsoleApplication1.CityInfo>::MoveNext()
IL_0029: brtrue.s IL_000f
IL_002b: leave.s IL_003b
} // end .try
finally
{
IL_002d: ldloca.s CS$5$0000
IL_002f: constrained. valuetype [mscorlib]System.Collections.Generic.List`1/Enumerator<class ConsoleApplication1.CityInfo>
IL_0035: callvirt instance void [mscorlib]System.IDisposable::Dispose()
IL_003a: endfinally
} // end handler
2:for方法在查询数据时,System.Collections.Generic.List`1<class ConsoleApplication1.CityInfo>::get_Item(int32),比起foreach少了try的处理,最重要的是能够使用索引访问元素。
3:linq方式性能最差。
非常感谢各位朋友的指点,特别是代码中忘记加break,现在是修改代码后的测试结果:结果和上面一样。
总结:泛型集合如果数据量大,最好采用for循环查询,数据量少的话,用linq方式最佳,代码优雅且简洁。
题外话:其实针对这种泛型集合查询,如果想优化性能,最好不要存储成泛型集合,采用Dictionary或者是hashtable效果更佳。