Question

Quelle est la « meilleure » façon (en tenant compte à la fois de la vitesse et de la lisibilité) de déterminer si une liste est vide ?Même si la liste est du type IEnumerable<T> et n'a pas de propriété Count.

En ce moment, j'hésite entre ceci :

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

et ça:

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

Je suppose que la deuxième option est plus rapide, car elle reviendra avec un résultat dès qu'elle verra le premier élément, alors que la deuxième option (pour un IEnumerable) devra visiter chaque élément pour renvoyer le décompte.

Ceci étant dit, la deuxième option vous semble-t-elle aussi lisible ?Lequel préféreriez-vous ?Ou pouvez-vous penser à une meilleure façon de tester une liste vide ?

Modifier La réponse de @lassevk semble être la plus logique, associée à un peu de vérification à l'exécution pour utiliser si possible un décompte en cache, comme ceci :

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

    return !list.Any();
}
Était-ce utile?

La solution

Vous pourriez faire ceci :

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

Modifier:Notez que la simple utilisation de la méthode .Count sera rapide si la source sous-jacente possède réellement une propriété Count rapide.Une optimisation valide ci-dessus consisterait à détecter quelques types de base et à utiliser simplement la propriété .Count de ceux-ci, au lieu de l'approche .Any(), puis à revenir à .Any() si aucune garantie ne peut être faite.

Autres conseils

Je ferais un petit ajout au code sur lequel vous semblez avoir choisi :vérifie aussi ICollection, car cela est également implémenté par certaines classes génériques non obsolètes (c'est-à-dire, Queue<T> et Stack<T>).j'utiliserais aussi as au lieu de is comme c'est plus idiomatique et s'est avéré plus rapide.

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

LINQ lui-même doit faire une optimisation sérieuse autour de la méthode Count() d'une manière ou d'une autre.

Cela vous surprend-il ?J'imagine que pour IList implémentations, Count lit simplement le nombre d'éléments directement pendant que Any doit interroger le IEnumerable.GetEnumerator méthode, créez une instance et appelez MoveNext au moins une fois.

/EDIT @Matt :

Je ne peux que supposer que la méthode d'extension Count() pour IEnumerable fait quelque chose comme ceci :

Oui, bien sûr.C'est ce que je voulais dire.En fait, il utilise ICollection au lieu de IList Mais le résultat est le même.

Je viens d'écrire un test rapide, essayez ceci :

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

La seconde est presque trois fois plus lente :)

En essayant à nouveau le test du chronomètre avec une pile ou un tableau ou d'autres scénarios, cela dépend vraiment du type de liste qui semble - car ils prouvent que Count est plus lent.

Donc je suppose que cela dépend du type de liste que vous utilisez !

(Juste pour souligner, j'ai mis plus de 2000 objets dans la liste et le compte était encore plus rapide, contrairement aux autres types)

List.Count est O(1) selon la documentation de Microsoft :
http://msdn.microsoft.com/en-us/library/27b47ht3.aspx

alors utilise simplement List.Count == 0 c'est beaucoup plus rapide qu'une requête

En effet, il possède une donnée membre appelée Count qui est mise à jour chaque fois que quelque chose est ajouté ou supprimé de la liste. Ainsi, lorsque vous appelez List.Count il n'est pas nécessaire de parcourir chaque élément pour l'obtenir, il renvoie simplement le membre de données.

La deuxième option est beaucoup plus rapide si vous avez plusieurs éléments.

  • Any() revient dès qu'un article est trouvé.
  • Count() doit continuer à parcourir toute la liste.

Par exemple, supposons que l'énumération comporte 1 000 éléments.

  • Any() vérifierait le premier, puis retournerait vrai.
  • Count() renverrait 1000 après avoir parcouru toute l’énumération.

C'est potentiellement pire si vous utilisez l'un des remplacements de prédicat - Count() doit toujours vérifier chaque élément, même s'il n'y a qu'une seule correspondance.

Vous vous habituez à utiliser Any one - cela a du sens et est lisible.

Une mise en garde : si vous avez une liste, plutôt qu'un simple IEnumerable, utilisez la propriété Count de cette liste.

@Konrad ce qui me surprend c'est que dans mes tests, je passe la liste dans une méthode qui accepte IEnumerable<T>, le runtime ne peut donc pas l'optimiser en appelant la méthode d'extension Count() pour IList<T>.

Je ne peux que supposer que la méthode d'extension Count() pour IEnumerable fait quelque chose comme ceci :

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

...en d'autres termes, un peu d'optimisation de l'exécution pour le cas particulier de IList<T>.

/EDIT @Konrad +1 mec - tu as raison, il est plus probable que ce soit le cas ICollection<T>.

Ok, et celui-ci ?

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

MODIFIER:Je viens de réaliser que quelqu'un a déjà esquissé cette solution.Il a été mentionné que la méthode Any() ferait cela, mais pourquoi ne pas le faire vous-même ?Salutations

Une autre idée:

if(enumerable.FirstOrDefault() != null)

Cependant, j’aime davantage l’approche Any().

C'était essentiel pour que cela fonctionne avec Entity Framework :

var genericCollection = list as ICollection<T>;

if (genericCollection != null)
{
   //your code 
}

Si je vérifie avec Count() Linq exécute un "SELECT COUNT(*).." dans la base de données, mais je dois vérifier si les résultats contiennent des données, j'ai décidé d'introduire FirstOrDefault() au lieu de Count();

Avant

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

if (cfop.Count() > 0)
{
    var itemCfop = cfop.First();
    //....
}

Après

var cfop = from tabelaCFOPs in ERPDAOManager.GetTable<TabelaCFOPs>()

var itemCfop = cfop.FirstOrDefault();

if (itemCfop != null)
{
    //....
}
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);
    }

Voici ma mise en œuvre de la réponse de Dan Tao, permettant un prédicat :

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

myList.ToList().Count == 0.C'est tout

Cette méthode d'extension fonctionne pour moi :

public static bool IsEmpty<T>(this IEnumerable<T> enumerable)
{
    try
    {
        enumerable.First();
        return false;
    }
    catch (InvalidOperationException)
    {
        return true;
    }
}
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top