代码之家  ›  专栏  ›  技术社区  ›  Matt Hamilton

使用LINQ检查列表是否为空

  •  122
  • Matt Hamilton  · 技术社区  · 16 年前

    确定列表是否为空的“最佳”(同时考虑速度和可读性)方法是什么?即使列表是类型 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();
    }
    
    16 回复  |  直到 9 年前
        1
  •  97
  •   Lasse V. Karlsen    16 年前

    你可以这样做:

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

    编辑 :请注意,如果基础源实际具有fast count属性,则只使用.count方法将很快。上面的一个有效的优化方法是检测一些基类型并简单地使用那些基类型的.count属性,而不是.any()方法,但如果不能保证,则返回到.any()。

        2
  •  14
  •   Dan Tao    14 年前

    我会在您似乎已经确定的代码上做一个小的添加:也检查 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();
    }
    
        3
  •  8
  •   Konrad Rudolph    16 年前

    linq本身必须以某种方式围绕count()方法进行一些严重的优化。

    这让你吃惊吗?我想是为了 IList 实施, Count 只需直接读取元素数,而 Any 必须查询 IEnumerable.GetEnumerator 方法,创建实例并调用 MoveNext 至少一次。

    编辑@ Matt:

    我只能假设IEnumerable的count()扩展方法正在执行以下操作:

    是的,当然有。这就是我的意思。实际上,它使用 ICollection 而不是 伊利斯特 但结果是一样的。

        4
  •  6
  •   crucible    16 年前

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

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

    第二个几乎慢了三倍。)

    用堆栈、数组或其他场景再次尝试秒表测试,这实际上取决于列表的类型,因为它们证明了计数要慢一些。

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

    (只是指出,我把2000多个对象放在列表中,计数速度仍然更快,与其他类型相反)

        5
  •  4
  •   stephenkelzer    9 年前

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

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

    这是因为它有一个名为count的数据成员,每当列表中添加或删除某些内容时,该成员都会更新,所以当您调用 清单 它不需要遍历每个元素就可以得到它,它只返回数据成员。

        6
  •  3
  •   Matt Hamilton    16 年前

    @让我吃惊的是,在我的测试中,我将列表传递给一个接受 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;T & GT; .

    /编辑@konrad+1伙伴-你说的对,更有可能是在 ICollection<T> .

        7
  •  2
  •   Keith    16 年前

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

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

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

    • 任意() 会检查第一个,然后返回true。
    • 计数() 将在遍历整个枚举后返回1000。

    如果使用其中一个谓词重写,则情况可能更糟-count()仍然必须检查每个项,即使只有一个匹配项。

    你习惯了使用任何一个-它确实有意义并且可读。

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

        8
  •  1
  •   Jonny Dee    15 年前

    好的,那么这个怎么样?

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

    编辑:我刚刚意识到有人已经画出了这个解决方案的草图。有人提到any()方法可以做到这一点,但为什么不自己做呢?当做

        9
  •  1
  •   Sam Holder    14 年前

    另一个想法:

    if(enumerable.FirstOrDefault() != null)
    

    不过,我更喜欢any()方法。

        10
  •  1
  •   Misam    12 年前

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

    var genericCollection = list as ICollection<T>;
    
    if (genericCollection != null)
    {
       //your code 
    }
    
        11
  •  0
  •   gandarez    13 年前

    如果我用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
  •  0
  •   DaveShaw Thishin    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
  •  0
  •   devuxer    12 年前

    下面是我对丹涛答案的实现,允许一个谓词:

    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
  •  -1
  •   Sergey Glotov Nitesh Khosla    12 年前
    List<T> li = new List<T>();
    (li.First().DefaultValue.HasValue) ? string.Format("{0:yyyy/MM/dd}", sender.First().DefaultValue.Value) : string.Empty;
    
        15
  •  -3
  •   HaveNoDisplayName    9 年前

    myList.ToList().Count == 0 . 这就是全部

        16
  •  -5
  •   Jonny Dee    15 年前

    此扩展方法适用于我:

    public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
    {
        try
        {
            enumerable.First();
            return false;
        }
        catch (InvalidOperationException)
        {
            return true;
        }
    }