哪种方式查询泛型集合性能上最佳
这篇文章我来分析下对于泛型集合,采取不同的查询方式在性能上会有什么样的影响?
这里有一个城市简单信息的实体类:
代码
class
CityInfo
{
public
int
CityID
{
get
;
set
;
}
public
string
CityName
{
get
;
set
;
}
public
string
CityNameEn
{
get
;
set
;
}
public
string
CityAddress
{
get
;
set
;
}
}
我们构造一个特别大的城市泛型类:
代码
int
i
=
100000
;
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:
代码
static
CityInfo GetCityInfoByForeach(List
<
CityInfo
>
list,
int
CityID)
{
CityInfo info
=
new
CityInfo();
foreach
(var item
in
list)
{
if
(item.CityID
==
CityID)
{
info
=
item;
breake;
}
}
return
info;
}
方法二:for循环:
代码
static
CityInfo GetCityInfoByFor(List
<
CityInfo
>
list,
int
CityID)
{
CityInfo info
=
new
CityInfo();
for
(
int
i
=
0
; i
<
list.Count;i
++
)
{
if
(list[i].CityID
==
CityID)
{
info
=
list [i];
breake;
}
}
return
info;
}
方法三:Linq查询:
代码
static
CityInfo GetCityInfoByLinq(List
<
CityInfo
>
list,
int
CityID)
{
CityInfo info
=
new
CityInfo();
info
=
list.Where(p
=>
p.CityID
==
CityID).FirstOrDefault();
return
info;
}
然后随机产生一个城市ID,分别针对上面三种方式调用500次,这里何用老赵的CodeTimer来显示信息,执行结果如下:
代码
CodeTimer.Time(
"
GetCityInfoByForeach
"
,
500
, ()
=>
GetCityInfoByForeach(list, m.Next(i
-
1
)));
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块,这也是需要消耗部分性能的。
代码
.
try
{
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效果更佳。