面试问题:.Any()与if (.Length > 0)在测试集合是否有元素方面的区别

22
在最近的一次采访中,我被问到.Any().Length > 0之间的区别,以及在测试集合是否有元素时为什么会使用它们中的任何一个。
这让我有点困惑,因为这似乎有点显而易见,但感觉可能我漏掉了些什么。
我建议您在只需知道集合中是否有元素时使用.Length,而在希望过滤结果时使用.Any()
很可能.Any()也会受到性能损失,因为它必须在内部进行循环/查询。

只是好奇,.Count() 是一个选项吗?如果可用,它将使用 .Length(实际上是从 ICollection 接口的 .Count);如果不可用,则枚举……像这样的 .Any() 没有快捷方式。 - Nick Craver
我一直认为如果本地属性可用,例如数组的.length和集合的.count,则它们会被预先计算。因此,我只会在它们不可用或者我想要进行条件.any(lambda)检查时使用.any() - Nope
9个回答

30

Length只存在于某些集合类型,如Array

Any是可用于任何实现IEnumerable<T>的集合的扩展方法。

如果有Length属性,则可以使用它,否则使用Any


可能.Any()也会带来性能问题,因为它必须在内部执行循环/查询。

Enumerable.Any不会进行循环。它获取一个迭代器并检查MoveNext是否返回true。以下是来自.NET Reflector的源代码。

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;
}

还有一个IQueryable<T>版本,但我不确定OP在谈论哪个,也许两个都是。编辑:通过集合,我猜你是对的,只是linq-to-entities。 - Nick Craver

14
我猜面试官可能想问的是检查Any()Count() > 0之间的区别(而不是Length > 0)。基本上,情况如下:Any()将枚举单个项来尝试确定集合是否有任何成员。(有一个重载方法使用Func<T, bool>来检查给定条件,但我猜面试官指的是不带参数的Any()版本。)这使得时间复杂度为O(1)。Count()首先将检查T[]ICollectionICollection<T>中的LengthCount属性。通常这会是O(1)。但是,如果不可用,它将通过枚举整个集合来计算项目数。这将是O(n)。CountLength属性如果可用,与Any()一样,很可能是O(1),并且可能表现更好,因为根本不需要枚举。但是Count()扩展方法不能保证这一点。因此,有时是O(1),有时是O(n)。假设你正在处理一个不明确实现ICollection<T>IEnumerable<T>,并且你不知道它是否实现了, 如果你的意图仅是确保集合不为空,则使用Any()Count() > 0要好得多。

我赞成使用Any(),但对于数组/集合,使用Count或Length是合理的,因为新员工可以轻松理解它们,尤其是那些来自其他框架的员工。 - Ronald Abellano

1

.Length... System.Array.Any... IEnumerable(扩展方法)。

我更喜欢在可以找到它的情况下使用“length”。属性比任何方法调用都要轻量级。

尽管如此,“Any”的实现不会做更多的事情,只会执行下面提到的代码。

 private static bool Any<T>(this IEnumerable<T> items)
        {
            return items!=null && items.GetEnumerator().MoveNext();
        }

另外,一个更好的问题可能是".Count"和".Length"之间的区别,你觉得呢 :)。

有趣...你能解释一下:"属性比任何方法调用都要轻量级。" - Amy B
@David:属性通常是对字段(内存中的值)进行get/set,而方法则可以自由地执行所有可能的计算。我总是会依赖于对get_Property的调用,而不是方法调用。(例如:obj.ComputedValue vs ComputeValue()) - Manish Basantani
1
因此,这只是惯例或指南,而不是任何技术原因。属性可以违反惯例实现(例如,属性可以调用方法)。 - Amy B
为什么要进行这个检查,items!=null?因为如果items为null,调用无论如何都会失败。 - Code Name Jack

1

Length是数组类型的属性,而Any()Enumerable的扩展方法。因此,在处理数组时只能使用Length。在处理更抽象的类型(例如 IEnumerable<T>)时,可以使用Any()。


0

我认为这是一个更普遍的问题,当我们有两种表达方式时该选择哪一种。 在这种情况下,我建议引用Peter Norvig在他的书PAIP中的话:“具体化”

具体化意味着使用最能描述你正在做的事情的方法。 因此,你想要表达的是:

collection.isEmpty()

如果你没有这样的结构,我将选择社区常用的成语。 对我来说,.Length > 0 不是最好的选择,因为它强制规定您可以对对象进行大小调整。 假设您实现了无限列表。.Lenght 显然不起作用。

0
我们知道.Length仅用于数组,而.Any()用于IEnumerable集合。
您可以将.Count替换为.Length,并且对于使用IEnumberable集合的问题,您将得到相同的答案。
.Both Any()和.Count在开始枚举器之前执行空值检查。因此,就性能而言,它们是相同的。
至于数组,让我们假设我们有以下行:
int[] foo = new int[10];

这里 foo.Length 是 10。虽然这是正确的,但可能不是您要寻找的答案,因为我们尚未向数组添加任何内容。如果 foo 为空,它将抛出异常。


0

这听起来很像 Stackoverflow 上的一个问题,关于使用 .Count 和 .Any 来检查结果是否存在的区别:在 Linq-to-xml 中检查结果是否存在

在这种情况下,最好使用 Any 而不是 Count,因为 Count 会迭代 IEnumerable 的所有元素。


1
这是不正确的,.Count() 使用ICollection(通用或非通用)上的.Count(例如在Array上获取.Length)属性,而.Any()则不使用,它将进行枚举,您可以启动Reflector来查看此信息,在 .Net 4中仍然如此。在IEnumerable<T>上,IQueryable<T>是另一回事。 - Nick Craver
很有趣-我将以下内容解释为Count将枚举并且Any不会:http://rapidapplicationdevelopment.blogspot.com/2009/07/ienumerablecount-is-code-smell.html - Kris C
@Nick:Any()(至少重载不带参数的)将枚举一个项目。如果基础的 IEnumerable<T> 不是 ICollection<T>,则 Count() 将枚举所有项目。因此,如果集合类型未知,则使用 Any()Count() > 0 更有意义。 - Dan Tao
1
@Nick:我认为“除非你为他人编写框架”这部分使这种可能性听起来比实际更少。您可能正在编写通用方法或库;它不必是整个框架。 话虽如此,您说的打开迭代器的成本是未知的;但访问某些第三方ICollection <T>Count属性的成本也是未知的。总会有未知因素;对于未知类型的IEnumerable <T>,对我来说Any()似乎是更好的教育猜测。 - Dan Tao
@Dan - 所有的观点都很好,我写的大部分内容都是为了自己或本地使用,因此未知类型相对较少,但我明白这并非总是如此。我绝对同意面试官应该知道每种方法的区别和可能的成本,这应该是整个面试的重点...不幸的是,最近我们面试的许多程序员一窍不通 :-/ - Nick Craver
显示剩余2条评论

0

关于讨论的目的。所有源代码都可用,因此您知道: 如果您正在使用具有Length或Count属性的实例,则始终具有O(1)。该值是您实例的成员,您可以直接与任何其他值进行比较。它只是两个值之间的比较操作。

扩展方法Any()总是首先创建一个新的迭代器,然后尝试获取第一个元素。它也是O(1),但由于每次调用Any()时都会分配一小部分内存用于此迭代器,因此会创建一个新的迭代器。在MoveNext()的内部实现中,该迭代器使用了2个比较操作和一个基于索引的访问列表,因为MoveNext还将当前值保存为成员以进行迭代(请参见List的枚举器的实现) 这就是为什么如果可以使用,则应优先使用Count或Length属性。


1
目前你的回答不够清晰。请编辑并添加更多细节,以帮助其他人理解它如何回答所提出的问题。你可以在帮助中心找到有关如何撰写好答案的更多信息。 - Community

-2

.Length 迭代集合并返回元素数量。复杂度为 O(n)

.Any 检查集合是否至少有一个项目。复杂度为 O(1)


2
当可用时,Length属性保证是O(1)操作。另一方面,Count取决于集合类型(对于列表而言,它是O(1))。 - Matthieu

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接