使用 LINQ 检查列表是否为空

确定列表是否为空的“最佳”(考虑速度和可读性)方法是什么?即使列表是 IEnumerable<T>类型且没有 Count 属性。

现在我在这两者之间辗转反侧:

if (myList.Count() == 0) { ... }

还有这个:

if (!myList.Any()) { ... }

我猜测第二个选项会更快,因为它一看到第一个项目就会返回结果,而第二个选项(对于 IEnumable)需要访问每个项目才能返回计数。

话虽如此,你觉得第二个选项看起来可读吗?你喜欢哪个?或者你能想到一个更好的方法来测试一个空列表吗?

编辑 @lassevk 的响应似乎是最合乎逻辑的,如果可能的话,再加上一点运行时检查来使用缓存计数,如下所示:

public static bool IsEmpty<T>(this IEnumerable<T> list)
{
if (list is ICollection<T>) return ((ICollection<T>)list).Count == 0;


return !list.Any();
}
156588 次浏览

你可以这样做:

public static Boolean IsEmpty<T>(this IEnumerable<T> source)
{
if (source == null)
return true; // or throw an exception
return !source.Any();
}

编辑 : 注意,只需使用。如果基础源实际上具有 fast Count 属性,则 Count 方法将是快速的。上面的一个有效的优化方法是检测一些基本类型,然后简单地使用。属性的值,而不是。任何()方法,但随后退回到。如果不能作出保证,任何()。

我刚写了一个快速测试,试试这个:

 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();

第二个几乎慢了三倍:)

用堆栈或数组或其他场景再次尝试秒表测试,这实际上取决于它看起来的列表类型——因为它们证明 Count 比较慢。

所以我想这取决于您使用的列表的类型!

(只是为了指出,我把2000多个对象列表和计数更快,与其他类型相反)

如果您有多个项目,第二个选项会快得多。

  • 只要找到1个项目,Any()就返回。
  • Count()必须不断浏览整个列表。

例如,假设枚举有1000个项。

  • Any()将检查第一个,然后返回 true。
  • 在遍历整个枚举之后,Count()将返回1000。

如果您使用其中一个谓词重写-Count ()仍然必须检查每一个项,即使只有一个匹配项。

你会习惯使用 Anyone-它确实有意义并且可读。

一个警告——如果您有一个 List,而不仅仅是一个 IEnumable,那么使用该 List 的 Count 属性。

LINQ 本身必须围绕 Count ()方法以某种方式进行一些严肃的优化。

你很惊讶吗?我想象对于 IList实现,Count只是直接读取元素的数量,而 Any必须查询 IEnumerable.GetEnumerator方法,创建一个实例并至少调用 MoveNext一次。

编辑@Matt:

我只能假设 IEnumable 的 Count ()扩展方法是这样做的:

是的,当然。我就是这个意思。实际上,它使用 ICollection而不是 IList,但结果是相同的。

@ Konrad 让我惊讶的是,在我的测试中,我将列表传递给一个接受 IEnumerable<T>的方法,所以运行时不能通过调用 IList<T>的 Count ()扩展方法来优化它。

我只能假设 IEnumable 的 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>的特殊情况进行一些运行时优化。

编辑@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()方法。

我想对您似乎已经选定的代码做一个小小的补充: 也检查一下 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();
}

如果我使用 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)
{
//....
}

这对于使其与实体框架协同工作至关重要:

var genericCollection = list as ICollection<T>;


if (genericCollection != null)
{
//your code
}
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);
}
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;

下面是我对 Dan Tao 的回答的实现,考虑到一个谓词:

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

myList.ToList().Count == 0仅此而已