提出问题
本文使用下面的实例来说明问题,以下是实例的完整代码。
//************************************************************
//
// Sum应用示例代码
//
// Author:三五月儿
//
// Date:2014/09/10
//
// http://blog.csdn.net/yl2isoft
//
//************************************************************
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace LinqSumExp
{
class Program
{
static void Main(string[] args)
{
Console.WriteLine("数据准备中,请稍后...");
List<Score> scoreList = CreateScoreList();
Console.WriteLine("正在执行中,请稍后...");
Stopwatch sw1 = new Stopwatch();
sw1.Start();
//------------代码片段1--------------------start
int mathScoreSum1 = 0;
int chineseScoreSum1 = 0;
int engLishScoreSum1 = 0;
int physicsScoreSum1 = 0;
int chemistryScoreSum1 = 0;
int biologyScoreSum1 = 0;
foreach (var s in scoreList)
{
mathScoreSum1 += s.MathScore;
chineseScoreSum1 += s.ChineseScore;
engLishScoreSum1 += s.EngLishScore;
physicsScoreSum1 += s.PhysicsScore;
chemistryScoreSum1 += s.ChemistryScore;
biologyScoreSum1 += s.BiologyScore;
}
//------------代码片段1--------------------end
sw1.Stop();
TimeSpan ts1 = sw1.Elapsed;
Console.WriteLine("代码片段1的执行时间为:" + ts1.TotalMilliseconds);
Stopwatch sw2 = new Stopwatch();
sw2.Start();
//------------代码片段2--------------------start
int mathScoreSum2 = 0;
int chineseScoreSum2 = 0;
int engLishScoreSum2 = 0;
int physicsScoreSum2 = 0;
int chemistryScoreSum2 = 0;
int biologyScoreSum2 = 0;
mathScoreSum2 = scoreList.Sum(it => it.MathScore);
chineseScoreSum2 = scoreList.Sum(it => it.ChineseScore);
engLishScoreSum2 = scoreList.Sum(it => it.EngLishScore);
physicsScoreSum2 = scoreList.Sum(it => it.PhysicsScore);
chemistryScoreSum2 = scoreList.Sum(it => it.ChemistryScore);
biologyScoreSum2 = scoreList.Sum(it => it.BiologyScore);
//------------代码片段2--------------------end
sw2.Stop();
TimeSpan ts2 = sw2.Elapsed;
Console.WriteLine("代码片段2的执行时间为:" + ts2.TotalMilliseconds);
}
static List<Score> CreateScoreList()
{
List<Score> scoreList = new List<Score>();
Random rd = new Random();
for (int i = 0; i < 100; i++)
{
Score s = new Score();
s.StudentId = i;
s.StudentName = "s" + i.ToString();
s.MathScore = rd.Next(0, 100);
s.ChineseScore = rd.Next(0, 100);
s.EngLishScore = rd.Next(0, 100);
s.PhysicsScore = rd.Next(0, 100);
s.ChemistryScore = rd.Next(0, 100);
s.BiologyScore = rd.Next(0, 100);
scoreList.Add(s);
}
return scoreList;
}
}
public class Score
{
public int StudentId { get; set; }
public string StudentName { get; set; }
public int MathScore { get; set; }
public int ChineseScore { get; set; }
public int EngLishScore { get; set; }
public int PhysicsScore { get; set; }
public int ChemistryScore { get; set; }
public int BiologyScore { get; set; }
}
}
实例中先定义Score类,使用Score类来保存学生各门功课的成绩,其中属性MathScore、ChineseScore、EngLishScore、PhysicsScore、ChemistryScore、BiologyScore分别用来保存数学、语文、英语、物理、化学、生物的成绩。接着,在CreateScoreList方法中生成包含100个Score对象的集合。最后,使用代码片段1和代码片段2来计算集合中各门功课的总和,其中,代码片段1通过遍历集合的方法来求和,而代码片段2使用Linq的求和方法Sum来实现求和。很显然,两种方法都可以完成求和这个基本功能。但是,在实际开发中,很多时候除了实现基本功能外,还需要考虑其他许多东西,比如性能。那么,这里我就弱弱的问一句:那种方法性能更好?
答案揭晓
大家请看黑板。(呵呵,是不是好黑好黑的一块板子啊)
图1 程序运行结果图
从程序的执行结果来看,方法1的性能更好。
你可能会说,仅仅通过一次结果无法得出这个结论,因为偶然性。
为了让你心服口服,那我就继续执行,执行,再执行。
经过我n多次重复试验(n到底有多大,你猜),发现:每一次都是方法1耗时更少,方法1性能好于方法2。
为了让我们的实验更具有说服力,增加数据量为1000,10000,100000来执行程序,下面是实验结果:
- 1000次:方法1--1.2628;方法2--3.3205
- 10000次:方法1--3.1866;方法2--8.8877
- 100000次:方法1--15.3749;方法2--65.5758
还是方法1耗时更少吧?
你要是还不服,那我也没办法了,反正我是相信这个结果了。
那么为什么会有这个结果呢?
原因说明
查看Linq求和方法Sum的源码,代码如下所示:
public static int Sum(this IEnumerable<int> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
int num = 0;
foreach (int num2 in source)
{
num += num2;
}
return num;
}
很显然,每调用一次Linq的求和方法都会遍历一次集合,所以,方法2会遍历集合5次,而方法1只需遍历集合1次,这就是原因所在。
当然,需要求和的字段越多,数据量越大,两种方法的性能差距将越大。其实,除了在使用Linq的求和方法Sum时会遇见这个问题外,在使用Linq中其他扩展方法时也会遇到这种问题,希望大家以后注意了。