使用 LINQ 检查列表是不是为空
Posted
技术标签:
【中文标题】使用 LINQ 检查列表是不是为空【英文标题】:Checking if a list is empty with LINQ使用 LINQ 检查列表是否为空 【发布时间】:2010-09-07 15:58:55 【问题描述】:确定列表是否为空的“最佳”(同时考虑速度和可读性)方法是什么?即使列表是 IEnumerable<T>
类型并且没有 Count 属性。
现在我在这之间折腾:
if (myList.Count() == 0) ...
还有这个:
if (!myList.Any()) ...
我的猜测是第二个选项更快,因为它会在看到第一个项目后立即返回结果,而第二个选项(对于 IEnumerable)将需要访问每个项目以返回计数.
话虽如此,第二个选项对您来说是否可读?你更喜欢哪个?或者您能想出更好的方法来测试空列表吗?
编辑 @lassevk 的响应似乎是最合乎逻辑的,再加上一些运行时检查以尽可能使用缓存计数,如下所示:
public static bool IsEmpty<T>(this IEnumerable<T> list)
if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;
return !list.Any();
【问题讨论】:
最好不要混合使用is
和cast
,而是使用as
和null
检查:ICollection<T> collection = list as ICollection<T>; if (collection != null) return colllection.Count;
为什么要多写一个方法? list.Any()
不等于list.IsEmpty
吗?框架方法应该被优化——只有当你发现它是一个性能瓶颈时才值得写一个新的。
是否有人费心衡量他们建议的实现的性能,还是每个人都只是提出想法?
我建议向 .NET Core 类库添加 IsEmpty
扩展方法的问题。 github.com/dotnet/corefx/issues/35054如果您喜欢并同意,请查看并投票。
【参考方案1】:
你可以这样做:
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() 。
【讨论】:
或者使用一行并返回 (source==null) ?真:!source.Any(); (如果你没有抛出异常) 我会说,是的,为 null 抛出异常,然后添加第二个扩展方法,称为IsNullOrEmpty()
。
public static Boolean IsNullOrEmptyreturn !source?.Any() ?? true;
@ricksmt 感谢您的更新!我一定会使用它!【参考方案2】:
我会在您似乎已经确定的代码上做一点补充:还要检查ICollection
,因为即使是一些非过时的泛型类也实现了这一点(即Queue<T>
和Stack<T>
)。我也会使用as
而不是is
,因为它更习惯用法和has been shown to be faster。
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();
【讨论】:
我喜欢这个答案。一个警告是,当某些集合没有完全实现诸如NotSupportedException
或NotImplementedException
之类的接口时,它们会抛出异常。当我发现我使用的集合引发了 Count 异常时,我第一次使用了您的代码示例(谁知道...)。
我理解为什么这样的优化对于像 Count() 这样需要枚举所有元素的方法很有用。但是 Any() 最多只需要枚举一个元素,所以我在这里看不到重点。另一方面,您添加的强制转换和 if 语句是您必须在每次调用时支付的固定成本。【参考方案3】:
LINQ 本身必须以某种方式围绕 Count() 方法进行一些认真的优化。
这让你感到惊讶吗?我想对于IList
实现,Count
只是直接读取元素的数量,而Any
必须查询IEnumerable.GetEnumerator
方法,创建一个实例并调用MoveNext
至少一次。
/编辑@马特:
我只能假设 IEnumerable 的 Count() 扩展方法正在做这样的事情:
是的,当然可以。这就是我的意思。实际上,它使用ICollection
而不是IList
,但结果是一样的。
【讨论】:
【参考方案4】:我刚刚写了一个快速测试,试试这个:
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 更慢。
所以我想这取决于您使用的列表类型!
(需要指出,我在 List 中放了 2000 多个对象,计数仍然更快,与其他类型相反)
【讨论】:
Enumerable.Count<T>()
对ICollection<T>
有特殊处理。如果您尝试使用其他而不是基本列表的内容,我希望您会看到显着不同(较慢)的结果。不过,Any()
将保持不变。
我必须同意马克的观点;这不是一个真正公平的测试。
知道为什么Enumerable.Any<T>()
对ICollection<T>
没有特殊处理吗?当然,无参数的Any()
也可以检查Count
属性中的ICollection<T>
吗?【参考方案5】:
根据微软的文档,List.Count
是 O(1):http://msdn.microsoft.com/en-us/library/27b47ht3.aspx
所以只需使用List.Count == 0
它比查询快得多
这是因为它有一个名为 Count 的数据成员,它会在列表中添加或删除任何内容时更新,因此当您调用 List.Count
时,它不必遍历每个元素来获取它,它只是返回数据成员。
【讨论】:
如果它是“IEnumerable”,那么没有。 (对于初学者来说,IEnumerable 没有“Count”属性,它有一个 Count() 方法。)。调用“Count()”将要求 IEnumerable 检查列表中的每个元素。而“任何”只要找到 1 个元素就会返回。 取决于数据源。如果你使用 yield 来构建一个 IEnumerable,它必须遍历 IEnumerable 才能知道它的大小。所以在某些情况下它只是 O(1)。它并不总是 O(1)。【参考方案6】:如果您有多个项目,第二个选项会更快。
Any()
找到 1 项后立即返回。
Count()
必须继续遍历整个列表。
例如假设枚举有 1000 个项目。
Any()
会检查第一个,然后返回 true。
Count()
会在遍历整个枚举后返回 1000。
如果您使用谓词覆盖之一,情况可能会更糟 - Count() 仍然必须检查每个项目,即使它只有一个匹配项。
您习惯于使用 Any one - 它确实有意义且可读。
一个警告 - 如果您有一个列表,而不仅仅是一个 IEnumerable,那么请使用该列表的 Count 属性。
【讨论】:
Any() 和 Count() 之间的区别似乎很明显,但 @crucible 的分析代码似乎表明 Count() 对于 IEnumerable@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 mate - 你说得对,它更有可能出现在 ICollection<T>
上。
【讨论】:
【参考方案8】:好的,那这个呢?
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
return !enumerable.GetEnumerator().MoveNext();
编辑:我刚刚意识到有人已经草拟了这个解决方案。有人提到 Any() 方法会这样做,但为什么不自己做呢?问候
【讨论】:
但是当你正确地将它包含在一个using
块中时它变得不那么简洁,因为否则你已经构造了一个 IDisposable
对象然后放弃它。然后,当然,当您使用已经存在的扩展方法并将其更改为 return !enumerable.Any()
(正是这样做的)时,它会变得更加简洁。
为什么要重写一个已经存在的方法?如前所述,Any()
正是这样做的,因此使用另一个名称添加完全相同的方法只会令人困惑。【参考方案9】:
另一个想法:
if(enumerable.FirstOrDefault() != null)
不过我更喜欢 Any() 方法。
【讨论】:
如果你有一个第一个元素为空的非空列表怎么办?【参考方案10】:这对于让它与实体框架一起工作至关重要:
var genericCollection = list as ICollection<T>;
if (genericCollection != null)
//your code
【讨论】:
如何回答这个问题?集合中没有元素时不能为空。【参考方案11】:如果我用 Count() 检查 Linq 在数据库中执行“SELECT COUNT(*)..”,但我需要检查结果是否包含数据,我决定引入 FirstOrDefault() 而不是 Count();
之前
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()
if (cfop.Count() > 0)
var itemCfop = cfop.First();
//....
之后
var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()
var itemCfop = cfop.FirstOrDefault();
if (itemCfop != null)
//....
【讨论】:
【参考方案12】:private bool NullTest<T>(T[] list, string attribute)
bool status = false;
if (list != null)
int flag = 0;
var property = GetProperty(list.FirstOrDefault(), attribute);
foreach (T obj in list)
if (property.GetValue(obj, null) == null)
flag++;
status = flag == 0 ? true : false;
return status;
public PropertyInfo GetProperty<T>(T obj, string str)
Expression<Func<T, string, PropertyInfo>> GetProperty = (TypeObj, Column) => TypeObj.GetType().GetProperty(TypeObj
.GetType().GetProperties().ToList()
.Find(property => property.Name
.ToLower() == Column
.ToLower()).Name.ToString());
return GetProperty.Compile()(obj, str);
【讨论】:
【参考方案13】:这是我对丹涛答案的实现,允许使用谓词:
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source, Func<TSource, bool> predicate)
if (source == null) throw new ArgumentNullException();
if (IsCollectionAndEmpty(source)) return true;
return !source.Any(predicate);
public static bool IsEmpty<TSource>(this IEnumerable<TSource> source)
if (source == null) throw new ArgumentNullException();
if (IsCollectionAndEmpty(source)) return true;
return !source.Any();
private static bool IsCollectionAndEmpty<TSource>(IEnumerable<TSource> source)
var genericCollection = source as ICollection<TSource>;
if (genericCollection != null) return genericCollection.Count == 0;
var nonGenericCollection = source as ICollection;
if (nonGenericCollection != null) return nonGenericCollection.Count == 0;
return false;
【讨论】:
【参考方案14】:List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("0:yyyy/MM/dd", sender.First().DefaultValue.Value) : string.Empty;
【讨论】:
【参考方案15】:myList.ToList().Count == 0
。就是这样
【讨论】:
这是一个糟糕的主意。 ToList() 不应被过度使用,因为它会强制对可枚举进行全面评估。请改用 .Any()。【参考方案16】:这个扩展方法对我有用:
public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
try
enumerable.First();
return false;
catch (InvalidOperationException)
return true;
【讨论】:
避免这样使用异常。在上面的代码中,您预期某些定义明确的输入(即空枚举)会出现异常。因此,它们不是例外,它们是规则。这是对这种控制机制的滥用,会影响可读性和性能。为真正的例外情况保留使用例外。 一般来说,我会同意。但这是相应缺失 IsEmpty 方法的解决方法。而且我认为解决方法永远不是做某事的理想方法......此外,特别是在这种情况下,意图非常明确,并且“脏”代码被封装并隐藏在明确定义的位置。跨度> -1:如果您想这样做,请使用 FirstOrDefault(),如 ChulioMartinez 的回答。 异常处理的性能效率真的很差。所以这可能是这里最糟糕的解决方案。 "例外应该是例外。" - 不要将它们用于正常的程序流程。以上是关于使用 LINQ 检查列表是不是为空的主要内容,如果未能解决你的问题,请参考以下文章