讲解.NET 集合中使用Count属性和扩展方法Count<T>()区别
来源:互联网 发布:奥普和欧普哪个好 知乎 编辑:程序博客网 时间:2024/06/13 02:12
在.NET中System.Linq命名空间中有个扩展方法叫Count<T>()
,现在看下面的代码示例:
class Program { static void Main(string[] args) { var userList = new List<int>(); userList.Add(1); userList.Add(2); userList.Add(3); userList.Add(4); userList.Add(5); userList.Add(6); Console.WriteLine(userList.Count); Console.WriteLine(userList.Count()); var userList2 = userList.Select(i=>i);//返回的是IEnumerable<TResult>类型 Console.WriteLine(userList2.Count()); for (int i = 0; i < userList2.Count(); i++) { Console.WriteLine(userList[i]); } } }
上门代码通过调用了List的自身属性Count,然后又调用了Linq支持的扩展方法Count(),结果都是输出的6,可是它们背后有哪些不一样呢?在上门的for循环中,背后真正走了多少次呢?
我们先开Count本身的属性,这里只粘贴了核心源码,完整的地址自行可以查看http://referencesource.microsoft.com/#mscorlib/system/collections/generic/list.cs,aeb6ba6c11713802
[ContractPublicPropertyName("Count")] private int _size; public int Count { get { Contract.Ensures(Contract.Result<int>() >= 0); return _size; } } public void Add(T item) { if (_size == _items.Length) EnsureCapacity(_size + 1); _items[_size++] = item; _version++; }
我们通过上面可以看到,内部维护了一个_size字段来表示List中的数量,再向集合中添加元素时,已经进行统计过了。这时再取集合的数量时,时间复杂度就为O(1)。
然后我们在看下扩展方法Count<T>
源代码如下:
public static int Count<TSource>(this IEnumerable<TSource> source) { if (source == null) throw Error.ArgumentNull("source"); ICollection<TSource> collectionoft = source as ICollection<TSource>; if (collectionoft != null) return collectionoft.Count; ICollection collection = source as ICollection; if (collection != null) return collection.Count; int count = 0; using (IEnumerator<TSource> e = source.GetEnumerator()) { checked { while (e.MoveNext()) count++; } } return count; }
我们通过上面可以看到传入的集合元素如果不能转换为ICollection<T>
和ICollection 便会进行逐个元素进行遍历,然后返回最终的元素集合,这样的时间复杂度当然为O(n)。所以能直接调用集合属性获取数量,就尽量避免使用扩展方法Count<T>
。
说到这里,可能有的小伙伴问了,为何List<T>
能不能转换为ICollection<T>
?
我们来
public class List<T> : IList<T>, ICollection<T>, IList, ICollection, IReadOnlyList<T>, IReadOnlyCollection<T>, IEnumerable<T>, IEnumerable
public interface ICollection<T> : IEnumerable<T>, IEnumerable
这个ICollection<T>
继承自IEnumerable<T>
如果传入的List<T>
这种继承自ICollection<T>
的集合,当然直接就是调用对应的Count 属性,如果传入的是直接继承自IEnumerable<T>
的集合那么循环遍历获取集合中的数量,因为让一个父类转换为一个子类,理所当然这里一直就是null。
那么在上面的for循环中遍历,相当于每次遍历都需要获取一次集合中的数量,也就等于跑了6*6=36次,可见这是多么浪费CPU。即使List<T>
能够转换成功转换为ICollection<T>
但是在CPU里面也毕竟多了一次转换操作,转换后依旧是按照Count属性获取集合数量,还不如我们能直接获取就直接获取。
当然我们可以把for循环里面的改成如下遍历。如下代码:
for (int i = 0,len=userList2.Count(); i < len; i++) { Console.WriteLine(userList2[i]); } 或者直接使用foreach循环 foreach (var item in userList2) { Console.WriteLine(item); }
亦或者避开Select
延迟查询的特点(关于延迟查询,下篇文章会进行讲解),改成如下的方式:
var userList2 = userList.Select(i=>i).ToList();//返回的是List<TSource> ToList<TSource> for (int i = 0; i < userList.Count(); i++) { Console.WriteLine(userList[i]); }
通过上述源代码我们可以进一步了解到,作为一个公共方法肯定要满足大多数的需要(可以参考我这篇文章:http://blog.csdn.net/u010533180/article/details/50627771),所以这里使用了顶级的父类,就有了中间的两层转换操作,这也是基于性能方面考虑,比如传递过来的就是继承了ICollection接口,这样直接取出数量即可,不过大多数情况我们直接使用的是继承自IEnumerable<T>
接口的类,所以我们作为代码的搬运工要积极思考微软这套东西,持有怀疑性精神前进,这样我们会了解到更多的东西。
最后我想说的是我们要通过提供的源代码,积极优化我们书写的代码,哪怕一个稍微的转换,对你本身代码的性能就是一个很大的提升。
- 讲解.NET 集合中使用Count属性和扩展方法Count<T>()区别
- oracle中关于count(1)、count(*)、count(rowid)、count(某个字段)使用上的区别和性能问题
- oracle中关于count(1)、count(*)、count(rowid)、count(某个字段)使用上的区别和性能问题
- count(*),count(1)和count(field)区别
- count(*),count(1)和count(field)区别
- Oracle 中count(1) 和count(*) 的区别
- Oracle 中count(1) 和count(*) 的区别
- sql中count(*)和count(字段名)区别!
- SQL语句中count(1)和count(*)的区别
- Oracle 中count(1) 和count(*) 的区别
- Oracle 中count(1) 和count(*) 的区别
- Oracle 中count(1) 和count(*) 的区别
- Oracle 中count(1) 和count(*) 的区别
- SQL语句中count(1)和count(*)的区别
- Oracle 中count(1) 和count(*) 的区别
- //扩展对象的count方法
- sql中sum()和count()的区别
- myisam和innodb中count(*)的区别
- 给深度学习入门者的Python快速教程
- 背包入门--多重背包 hd 2079
- mysql两表间select/update/delect
- 指针
- 2014年good fellow提出GANs论文译本
- 讲解.NET 集合中使用Count属性和扩展方法Count<T>()区别
- java n*n的二维数组转置,不开辟新的空间
- Hadoop DataNode 无法连接到主机NameNode
- maven配置(阿里云)
- sklearn学习——SVM
- Android --- 常用的系统服务(二)
- SSM-----springMVC No mapping found for HTTP request with URI的问题
- mmap内存映射
- WMware中linux系统无wlan0网卡的解决方法