使用LINQ检查列表是否为空
确定列表是否为空的"最佳"方式(兼顾速度和可读性)是什么?即使列表的类型为IEnumerable <T>
并且没有Count属性。
现在,我在这之间折腾:
if (myList.Count() == 0) { ... }
还有这个:
if (!myList.Any()) { ... }
我的猜测是第二个选项更快,因为它会在看到第一个项目后立即返回结果,而第二个选项(对于IEnumerable)将需要访问每个项目以返回计数。
话虽这么说,第二种选择对我们来说可读吗?你更喜欢哪个?还是我们可以想到一种更好的方法来测试空列表?
Edit @lassevk的响应似乎是最合乎逻辑的,再加上一些运行时检查,以便在可能的情况下使用缓存的计数,如下所示:
public static bool IsEmpty<T>(this IEnumerable<T> list) { if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0; return !list.Any(); }
解决方案
回答
我们可以这样做:
public static Boolean IsEmpty<T>(this IEnumerable<T> source) { if (source == null) return true; // or throw an exception return !source.Any(); }
编辑:请注意,如果基础源实际上具有快速的Count属性,则仅使用.Count方法将很快。上面的有效优化将是检测一些基本类型,并简单地使用这些基本类型的.Count属性,而不是.Any()方法,但是如果无法保证,则退回到.Any()。
回答
我刚刚写了一个快速测试,请尝试以下操作:
IEnumerable<Object> myList = new List<Object>(); Stopwatch watch = new Stopwatch(); int x; watch.Start(); for (var i = 0; i <= 1000000; i++) { if (myList.Count() == 0) x = i; } watch.Stop(); Stopwatch watch2 = new Stopwatch(); watch2.Start(); for (var i = 0; i <= 1000000; i++) { if (!myList.Any()) x = i; } watch2.Stop(); Console.WriteLine("myList.Count() = " + watch.ElapsedMilliseconds.ToString()); Console.WriteLine("myList.Any() = " + watch2.ElapsedMilliseconds.ToString()); Console.ReadLine();
第二个几乎慢了三倍:)
再次尝试使用Stack或者数组或者其他方案进行秒表测试时,它实际上取决于显示的列表类型,因为它们证明Count较慢。
因此,我想这取决于我们使用的列表类型!
(只需指出,我将2000+个对象放入列表中,并且计数仍然更快,与其他类型相反)
回答
如果我们有多个项目,第二个选项会更快。
- 只要找到1个项目,
Any()
就会返回。 - Count()必须一直遍历整个列表。
例如,假设枚举有1000个项目。
Any()
将检查第一个,然后返回true。- 遍历整个枚举后,Count()将返回1000。
如果我们使用谓词重写之一,则Count()仍然必须检查每个项目,即使只有一个匹配项,也可能会更糟。
我们习惯于使用Any确实有意义并且可读。
一个警告,如果我们有一个列表,而不只是一个IEnumerable,则使用该列表的Count属性。
回答
LINQ itself must be doing some serious optimization around the Count() method somehow.
这会让我们感到惊讶吗?我想对于IList实现,Count只是直接读取元素数,而Any则必须查询IEnumerable.GetEnumerator方法,创建实例并至少调用一次MoveNext。
/编辑@Matt:
I can only assume that the Count() extension method for IEnumerable is doing something like this:
是的,当然可以。这就是我的意思。实际上,它使用的是" ICollection"而不是" IList",但是结果是相同的。
回答
@Konrad让我感到惊讶的是,在我的测试中,我将列表传递给了一个接受IEnumerable <T>的方法,因此运行时无法通过调用IList <T的Count()扩展方法来对其进行优化。 >`。
我只能假设IEnumerable的Count()扩展方法正在执行以下操作:
public static int Count<T>(this IEnumerable<T> list) { if (list is IList<T>) return ((IList<T>)list).Count; int i = 0; foreach (var t in list) i++; return i; }
……换句话说,是针对" IList <T>"的特殊情况的运行时优化。
/ EDIT @Konrad +1伴侣,很可能是在ICollection <T>
上。
回答
此扩展方法适用于我:
public static bool IsEmpty<T>(this IEnumerable<T> enumerable) { try { enumerable.First(); return false; } catch (InvalidOperationException) { return true; } }
回答
好的,那这个呢?
public static bool IsEmpty<T>(this IEnumerable<T> enumerable) { return !enumerable.GetEnumerator().MoveNext(); }
编辑:我刚刚意识到有人已经草绘了此解决方案。有人提到Any()方法可以做到这一点,但是为什么不自己做呢?问候
回答
另一个想法:
if(enumerable.FirstOrDefault() != null)
但是我更喜欢Any()方法。
回答
根据微软的文档,List.Count
是O(1):
http://msdn.microsoft.com/zh-CN/library/27b47ht3.aspx
所以只用List.Count == 0
它比查询快得多
这是因为它有一个名为Count的数据成员,该成员在每次添加或者删除列表时都会进行更新,因此,当我们调用List.Count时,它不必遍历每个元素来获取它,它只返回数据成员。
回答
我将对我们似乎已定下的代码进行一些补充:还检查ICollection
,因为即使某些非过时的泛型类(例如," Queue <T>"和Stack < T>
)。我也将使用as
代替is
,因为它更加惯用并且被证明是更快的。
public static bool IsEmpty<T>(this IEnumerable<T> list) { if (list == null) { throw new ArgumentNullException("list"); } var genericCollection = list as ICollection<T>; if (genericCollection != null) { return genericCollection.Count == 0; } var nonGenericCollection = list as ICollection; if (nonGenericCollection != null) { return nonGenericCollection.Count == 0; } return !list.Any(); }