1、count和any

今天看了鹤冲天的文章:Linq:切勿使用 Count() > 0 来判断集合非空   有所收获,写下文章总结一下:

先看如下代码:

        static void Main(string[] args)
{
Stopwatch watch = new Stopwatch();
watch.Start();
var nums = GetNums(, );
SomeAction(nums);
watch.Stop();
Console.WriteLine(watch.ElapsedMilliseconds);
Console.WriteLine("over");
Console.ReadKey();
}
public static IEnumerable<int> GetNums(int start, int count)
{
var end = start + count;
for (int i = start; i < end; i++)
yield return i;
}
public static void SomeAction<T>(IEnumerable<T> source)
{
//if (source.Count() > 0) //2233 毫秒
if (source.Any()) //3 毫秒
{
Console.WriteLine("have data");
}
}

自己调试了下,当yield循环低于100000次的话,count()和Any()的执行时间差不多,count()时间略大于any()所耗费的时间,当大于100000次的话,count()执行时间就远比Any()执行时间多很多!例如上面的程序执行结果,count耗时2233毫秒,any却只耗时3毫秒!这是为什么呢?我们来看下原因:分别反编译 Enumerate.Count 的源码和 Enumerate.Any的源码,如下:

Enumerate.Any 实现代码如下:

 public static bool Any<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
if (enumerator.MoveNext())
{
return true;
}
}
return false;
}

Enumerate.Count 的源码如下:

 public static int Count<TSource>(this IEnumerable<TSource> source)
{
if (source == null)
{
throw Error.ArgumentNull("source");
}
ICollection<TSource> is2 = source as ICollection<TSource>;
if (is2 != null)
{
return is2.Count;
}
ICollection is3 = source as ICollection;
if (is3 != null)
{
return is3.Count;
}
int num = ;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}

无论循环多少次,Any方法执行的时间总是最少的,大概10毫秒之内,我想大家从Any的源码可以知道这个原因,Any方法先判断集合是否为空,然后判断集合是否有数据,只进行一次movenext(),并未进行循环查询集合数量!所以耗费的时间当然一直是最少的了!

我们再看Count方法,同样是先进行集合判空操作,然后判断当前的集合source是否能转成ICollection类型,如果能转成,就直接返回此集合的Count属性,不会在进行下面的循环获取集合个数的操作了,为什么Count()方法比Any()方法执行的时间长呢?答案就在于此:因为GetNums方法中用yield返回纯粹的IEnumerable<int>类型,但是ICollection<T>是继承自IEnumerable<T>类型的,所以必然不好被转换,也就是Count()源码中的下面代码不会执行

     ICollection is3 = source as ICollection;
if (is3 != null)
{
return is3.Count;
}

上面的代码不会执行,那么必然会进行下面的代码操作,也就是循环获取集合的个数:

     int num = ;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;

这样必然会导致Count()方法耗时很久!

2、如果我们把GetNums进行如下修改,其他代码不动:

       public static IEnumerable<int> GetNums(int start, int count)
{
List<int> list = new List<int>();
var end = start + count;
for (int i = start; i < end; i++)
list.Add(i);
return list;
}

这时我们在执行程序,可以看到无论循环次数多少,Count和Any两者执行的时间差不多,大概是1315毫秒左右,其中1312毫秒用于list集合的装填工作,3毫秒用于Count或Any的判断时间,为什么会出现这种情况呢,我想通过上面大家都知道了,因为此时GetNums方法返回的是List<T>类型,此类型继承于ICollection,所以必然可以被转换,就会返回此类型的Count属性,Count 方法对 ICollection 进行了优化,直接访返回它的 Count 属性,也就是返回一个数,当然很快了,下面的循环获取集合的个数当然也就不会执行了,也就节约了时间。

3、对某人问题的思考

有人说:”我有个程序里经常要判集合里是否仅有一个元素,又不能用Count,不得已自己写了个扩展方法 bool Check(this IEnumerable<T> source, int n), 当读到第n+1个元素就直接返回false“

我想如果此人用的集合是继承于ICollection<T>的话,例如List<T>,那么是没必要对IEnumerable<T>进行扩展的,通过反编译Count源码可以知道,如果集合source可以转换成ICollection的话,是可以直接通过count属性获取到集合的总数量的,所以耗费的时间要不了多少,也就没必要对IEnumerable进行扩展了!反之,如果不是继承ICollection<T>的话,例如IEnumerable,就必须自己扩展IEnumerable方法了,如果还用count的话,就会造成循环了,也就降低了程序的运行效率了!

4、参考

http://www.cnblogs.com/ldp615/archive/2011/12/11/2284154.html#comment_tip  鹤冲天

作者:MrZivChu

2013-12-13   21:30:12

Linq中Count()和Any()引发的效率问题的更多相关文章

  1. MVC+Spring.NET+NHibernate .NET SSH框架整合 C# 委托异步 和 async /await 两种实现的异步 如何消除点击按钮时周围出现的白线? Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法

    MVC+Spring.NET+NHibernate .NET SSH框架整合   在JAVA中,SSH框架可谓是无人不晓,就和.NET中的MVC框架一样普及.作为一个初学者,可以感受到.NET出了MV ...

  2. Linq中关键字的作用及用法

    Linq中关键字的作用及用法 1.All:确定序列中的所有元素是否都满足条件.如果源序列中的每个元素都通过指定谓词中的测试,或者序列为空,则为 true:否则为 false. Demo: 此示例使用 ...

  3. Linq 中按照多个值进行分组(GroupBy)

    Linq 中按照多个值进行分组(GroupBy) .GroupBy(x => new { x.Age, x.Sex }) group emp by new { emp.Age, emp.Sex ...

  4. SQL语句中count(1)count(*)count(字段)用法的区别

    SQL语句中count(1)count(*)count(字段)用法的区别 在SQL语句中count函数是最常用的函数之一,count函数是用来统计表中记录数的一个函数, 一. count(1)和cou ...

  5. linq中AsEnumerable和AsQueryable的区别

    本文导读:用Linq来操作集合的时候会用到AsQueryable()和AsEnumerable(),何时该用AsQueryable()和何时该用AsEnumerable(),或许存在些疑惑.AsQue ...

  6. Linq中使用Left Join

    use Test Create table Student( ID ,) primary key, ) not null ) Create Table Book( ID ,) primary key, ...

  7. 简述Linq中.ToList(), .AsEnumerable(), AsQueryable()的区别和用法

    [TOC] 这3个方法的功能完全不同, 应按照具体业务场景使用. AsQueryable() 先说说什么是 IQueryable IQueryable 是当前的 data provider 返回的类型 ...

  8. linq中group by

    本文导读:LINQ定义了大约40个查询操作符,如select.from.in.where.group 以及order by,借助于LINQ技术,我们可以使用一种类似SQL的语法来查询任何形式的数据.L ...

  9. Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法

    Linq中 AsQueryable(), AsEnumerable()和ToList()的区别和用法:在写LINQ语句的时候,往往会看到AsEnumerable() ,AsQueryable() 和T ...

随机推荐

  1. cocosBuilder生成cbbi文件,绑定到cocos2d-x

      cocosBuilder生成cbbi文件,绑定到cocos2d-x 分类: Cocos2D-X2013-04-27 20:37 4651人阅读 评论(6) 收藏 举报 今天弄了一天.记录一下. 首 ...

  2. 单调队列 poj2823,fzu1894

    题目链接:http://poj.org/problem?id=2823 用RMQ超时了,我想应该是不会的,看discuss说,之前RMQ过了. 维护两个单调队列. 单调递减的队列,每插入一个时: 超过 ...

  3. 学习MyBatis之简单入门HelloWorld

    转:https://blog.csdn.net/gaomb_1990/article/details/78299784 一.准备 Eclipse:Luna Service Release 1 (4.4 ...

  4. 如何将Win7做为NTP服务器

    1. 修改注册表项    HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer   ...

  5. C语言中%p,%u,%lu都有什么用处

    %p表示输出这个指针, %d表示后面的输出类型为有符号的10进制整形, %u表示无符号10进制整型, %lu表示输出无符号长整型整数 (long unsigned)

  6. CSS font-size字体大小样式属性

    设置字体大小CSS单词与语法 基本语法结构: .divcss5{font-size:12px;}设置了文字大小为12px像素Font-size+字体大小数值+单位 单词:font-size语法:fon ...

  7. Java Web入门经典扫描版

    全书共分4篇19章,其中,第一篇为“起步篇”,主要包括开启JavaWeb之门.不可不知的客户端应用技术.驾驭JavaWeb开发环境.JavaWeb开发必修课之JSP语法等内容:第二篇为“核心篇”,主要 ...

  8. IDEA的使用方法(二)(代码提示)

    工欲善其事必先利其器: 在eclipse用久了代码提示对自己纯手搓代码还是不太好的,我就算是死,从楼上跳下去,也不会从了IDEA的代码提示的,在手搓了一个hello world之后,我写的是JAVA? ...

  9. 干货!一篇文章集合所有Linux基础命令,适合所有菜鸟学习和老手回顾!

    1 文件{ ls -rtl # 按时间倒叙列出所有目录和文件 ll -rt touch file # 创建空白文件 rm -rf 目录名 # 不提示删除非空目录(-r:递归删除 -f强制) dos2u ...

  10. php COM

    查看php.ini中是否已经开启了com.allow_dcom = true 从php/ext/里面查找一下有没有这个php_com_dotnet.dll这个文件 如果没有网上下载个,一般都会有的吧应 ...