【发布时间】:2018-07-20 01:56:49
【问题描述】:
IEnumerableCount() 和Length 之间的主要区别是什么?
【问题讨论】:
标签: c# .net ienumerable
IEnumerableCount() 和Length 之间的主要区别是什么?
【问题讨论】:
标签: c# .net ienumerable
通过在IEnumerable<T> 上调用Count,我假设您指的是System.Linq.Enumerable 上的扩展方法Count。 Length 不是IEnumerable<T> 上的方法,而是.Net 中数组类型的属性,例如int[]。
区别在于性能。 Length 属性保证为 O(1) 操作。 Count 扩展方法的复杂性因对象的运行时类型而异。它将尝试通过Count 属性转换为支持O(1) 长度查找的几种类型,例如ICollection<T>。如果没有可用的,那么它将枚举所有项目并计算它们的复杂度为 O(N)。
例如
int[] list = CreateSomeList();
Console.WriteLine(list.Length); // O(1)
IEnumerable<int> e1 = list;
Console.WriteLine(e1.Count()); // O(1)
IEnumerable<int> e2 = list.Where(x => x <> 42);
Console.WriteLine(e2.Count()); // O(N)
值e2 实现为不支持O(1) 计数的C# 迭代器,因此Count 方法必须枚举整个集合以确定它的长度。
【讨论】:
List<T> 没有 Length 属性 - 它有 Count 属性。数组有一个Length。 Count 在 ICollection 和 ICollection<T> 中指定(IList<T> 扩展)。
string[] 数组的上下文中...你会建议 Array.Length 来提高性能吗?
Count() 仍然是 O(1),因为数组将实现 ICollection<T> - 但它比直接使用 Length 效率低。如果您已经知道它是一个数组,我会使用 Length not 来提高效率,但因为我认为它更惯用。同样,对于编译时类型为ICollection<T> 的任何东西,我都会使用Count 属性。当编译时表达式的类型为IEnumerable<T>时,我会调用Count(),即使我知道它实际上是一个幕后数组。
System.Array 是一个更基础的类(类似于object);但在检查 MSDN 后,我发现它实现了几个“与集合相关”的接口。我是一名网络程序员,所以“计数”一开始更具有语义意义(比如 HTML 中的 header 与 div),但我理解你的意思,因为它在编译时明确是 string[] Length更有意义。
while(list.Count() > 0) 一些带有添加/删除操作的逻辑。
对Jon Skeet的评论稍加补充。
这里是Count()扩展方法的源码:
.NET 3:
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;
}
int num = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
.NET 4:
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 = 0;
using (IEnumerator<TSource> enumerator = source.GetEnumerator())
{
while (enumerator.MoveNext())
{
num++;
}
}
return num;
}
【讨论】:
ICollection 类型。 (因为它也有一个 Count 属性。)
use 要获得此方法使用的Error 类吗?除了 JScript 文档,我似乎在 MSDN 上的任何地方都找不到它。
长度是一个固定属性,例如一维数组或字符串。所以从来没有必要的计数操作(多维数组的大小是所有维度的乘积)。这里的 O(1) 操作意味着无论有多少元素,检索时间总是相同的。线性搜索(与此相反)是 O(n)。
ICollections 上的 Count 属性(例如 List 和 List
LINQ 的 Count() 方法基本上在每次调用它时都会进行迭代(除非对象是 ICollection 类型,然后请求 ICollection.Count 属性)。
请注意,IEnumerables 通常不是已定义的对象集合(如列表、数组、哈希表等),而是链接到后台操作,这些操作在请求时生成结果(称为延迟执行)。
通常,您有一个类似这样的 LINQ 语句的 SQL(延迟执行的典型应用):
IEnumerable<Person> deptLeaders =
from p in persons
join d in departments
on p.ID equals d.LeaderID
orderby p.LastName, p.FirstName
select p;
然后,有这样的代码:
if (deptLeaders.Count() > 0)
{
ReportNumberOfDeptLeaders(deptLeaders.Count());
if (deptLeaders.Count() > 20)
WarnTooManyDepartmentLeaders(deptLeaders.Count());
}
因此,当发出过多部门领导的警告时,.NET 会四次检查人员,将他们与部门领导核对,按名称对他们进行排序,然后对结果对象进行计数。
而且只有人和部门是预设值集合,而不是查询自己。
【讨论】:
.Count() > 0 与 .Any() 相同。
.Any() 不会比.Count() > 0 更有效率吗?顺便说一句,Resharper 总是抱怨.Count() > 0。这就是为什么我充满信心地提出它。