127

确定列表是否为空的“最佳”(同时考虑速度和可读性)方法是什么?即使列表是类型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();
}
4

16 回答 16

107

你可以这样做:

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

于 2008-09-03T08:38:51.650 回答
14

我将对您似乎已经确定的代码做一个小的补充:还要检查 for 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();
}
于 2010-08-26T15:17:13.230 回答
8

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

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

/编辑@马特:

我只能假设 IEnumerable 的 Count() 扩展方法正在做这样的事情:

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

于 2008-09-03T09:25:50.910 回答
6

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

 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 多个对象,计数仍然更快,与其他类型相反)

于 2008-09-03T08:39:30.400 回答
6

List.Count根据微软的文档是 O(1):http:
//msdn.microsoft.com/en-us/library/27b47ht3.aspx

所以只需使用List.Count == 0它比查询快得多

这是因为它有一个名为 Count 的数据成员,它会在列表中添加或删除某些内容时更新,因此当您调用List.Count它时不必遍历每个元素来获取它,它只返回数据成员。

于 2010-08-16T16:37:53.153 回答
3

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

  • Any()找到 1 个项目后立即返回。
  • Count()必须继续浏览整个列表。

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

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

如果您使用谓词覆盖之一,这可能会更糟 - Count() 仍然必须检查每个项目,即使它只有一个匹配项。

你习惯了使用 Any one - 它确实有意义并且可读。

一个警告 - 如果您有一个列表,而不仅仅是一个 IEnumerable,那么请使用该列表的 Count 属性。

于 2008-09-03T09:05:49.167 回答
3

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

我只能假设 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>.

于 2008-09-03T09:29:45.660 回答
1

好的,那么这个呢?

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    return !enumerable.GetEnumerator().MoveNext();
}

编辑:我刚刚意识到有人已经草拟了这个解决方案。有人提到 Any() 方法会这样做,但为什么不自己做呢?问候

于 2009-10-26T04:37:06.740 回答
1

另一个想法:

if(enumerable.FirstOrDefault() != null)

但是我更喜欢 Any() 方法。

于 2009-12-21T07:47:12.420 回答
1

这对于让它与 Entity Framework 一起工作至关重要:

var genericCollection = list as ICollection<T>;

if (genericCollection != null)
{
   //your code 
}
于 2011-12-15T21:48:16.143 回答
0

如果我用 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)
{
    //....
}
于 2011-08-05T14:39:14.600 回答
0
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);
    }
于 2012-03-19T13:00:18.203 回答
0

这是我对 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;
}
于 2012-09-02T15:54:45.677 回答
-1
List<T> li = new List<T>();
(li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;
于 2012-05-02T07:45:03.067 回答
-3

myList.ToList().Count == 0. 就这样

于 2015-05-05T14:50:03.180 回答
-5

这种扩展方法对我有用:

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
于 2009-10-25T11:59:31.780 回答