首页 > 代码库 > 使用Linq求和方法Sum计算集合中多个元素和时应该注意的性能问题

使用Linq求和方法Sum计算集合中多个元素和时应该注意的性能问题

提出问题

本文使用下面的实例来说明问题,以下是实例的完整代码。

//************************************************************    
//    
// 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中其他扩展方法时也会遇到这种问题,希望大家以后注意了。

 

 

 

 

 

 

 

使用Linq求和方法Sum计算集合中多个元素和时应该注意的性能问题