web-dev-qa-db-fra.com

Linq .Any VS .Exists - Quelle est la différence?

En utilisant Linq sur les collections, quelle est la différence entre les lignes de code suivantes?

if(!coll.Any(i => i.Value))

et

if(!coll.Exists(i => i.Value))

Mise à jour 1

Quand je désassemble .Exists, il semble qu'il n'y ait pas de code.

Mise à jour 2

Quelqu'un sait pourquoi il n'y a pas de code pour celui-ci?

385
Anthony D

Voir la documentation

List.Exists (Méthode Object - MSDN)

Détermine si la liste (T) contient des éléments correspondant aux conditions définies par le prédicat spécifié.

Cela existe depuis .NET 2.0, donc avant LINQ. Destiné à être utilisé avec le délégué Predicate , mais les expressions lambda sont compatibles avec les versions antérieures. En outre, juste la liste a cela (même pas IList)

IEnumerable.Any (Méthode d'extension - MSDN)

Détermine si un élément d'une séquence vérifie une condition.

Ceci est nouveau dans .NET 3.5 et utilise Func (TSource, bool) comme argument. Il était donc destiné à être utilisé avec les expressions lambda et LINQ.

En comportement, ceux-ci sont identiques.

394
Meinersbur

La différence est que Any est une méthode d'extension pour tout IEnumerable<T> défini sur System.Linq.Enumerable. Il peut être utilisé sur n'importe quelle instance IEnumerable<T>.

Exists ne semble pas être une méthode d'extension. Je suppose que coll est de type List<T>. Si tel est le cas, il s'agit d'une méthode d'instance qui fonctionne de manière très similaire à Any.

En bref, les méthodes sont essentiellement les mêmes. L'un est plus général que l'autre.

  • Any a aussi une surcharge qui ne prend aucun paramètre et cherche simplement n'importe quel élément de l'énumérable.
  • Exists n'a pas cette surcharge.
190
JaredPar

TLDR; Performances Any semble être plus lent (si j'ai correctement configuré cela pour évaluer les deux valeurs presque au même moment)

        var list1 = Generate(1000000);
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;
            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s +=" Any: " +end1.Subtract(start1);
            }

            if (!s.Contains("sdfsd"))
            {

            }

générateur de liste de test:

private List<string> Generate(int count)
    {
        var list = new List<string>();
        for (int i = 0; i < count; i++)
        {
            list.Add( new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    new RNGCryptoServiceProvider().GetBytes(cryptoResult);
                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray())); 
        }

        return list;
    }

Avec 10M de disques

"N'importe lequel: 00: 00: 00.3770377 Existe: 00: 00: 00.2490249"

Avec 5M records

"N'importe lequel: 00: 00: 00.0940094 Existe: 00: 00: 00.1420142"

Avec 1M records

"N'importe lequel: 00: 00: 00.0180018 Existe: 00: 00: 00.0090009"

Avec 500k, (j’ai également inversé l’ordre dans lequel ils ont été évalués pour voir s’il n’y avait pas d’opération supplémentaire associée à la première exécution.)

"Existe: 00: 00: 00.0050005 Any: 00: 00: 00.0100010"

Avec 100 000 enregistrements

"Existe: 00: 00: 00.0010001 N'importe lequel: 00: 00: 00.0020002"

Il semblerait que Any soit plus lent de l'ordre de 2.

Edit: Pour les enregistrements 5 et 10M, j’ai modifié la façon dont il génère la liste et Exists est soudainement devenu plus lent que Any, ce qui implique il y a quelque chose qui cloche dans la façon dont je teste.

Nouveau mécanisme de test:

private static IEnumerable<string> Generate(int count)
    {
        var cripto = new RNGCryptoServiceProvider();
        Func<string> getString = () => new string(
            Enumerable.Repeat("ABCDEFGHIJKLMNOPQRSTUVWXYZ", 13)
                .Select(s =>
                {
                    var cryptoResult = new byte[4];
                    cripto.GetBytes(cryptoResult);
                    return s[new Random(BitConverter.ToInt32(cryptoResult, 0)).Next(s.Length)];
                })
                .ToArray());

        var list = new ConcurrentBag<string>();
        var x = Parallel.For(0, count, o => list.Add(getString()));
        return list;
    }

    private static void Test()
    {
        var list = Generate(10000000);
        var list1 = list.ToList();
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;

            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s += " Any: " + end1.Subtract(start1);
            }

            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            if (!s.Contains("sdfsd"))
            {

            }
        }

Edit2: Ok afin d’éliminer toute influence liée à la génération des données de test, j’ai tout écrit dans un fichier et le lis maintenant à partir de là.

 private static void Test()
    {
        var list1 = File.ReadAllLines("test.txt").Take(500000).ToList();
        var forceListEval = list1.SingleOrDefault(o => o == "0123456789012");
        if (forceListEval != "sdsdf")
        {
            var s = string.Empty;
            var start1 = DateTime.Now;
            if (!list1.Any(o => o == "0123456789012"))
            {
                var end1 = DateTime.Now;
                s += " Any: " + end1.Subtract(start1);
            }

            var start2 = DateTime.Now;
            if (!list1.Exists(o => o == "0123456789012"))
            {
                var end2 = DateTime.Now;
                s += " Exists: " + end2.Subtract(start2);
            }

            if (!s.Contains("sdfsd"))
            {
            }
        }
    }

10M

"N'importe lequel: 00: 00: 00.1640164 Existe: 00: 00: 00.0750075"

5M

"N'importe lequel: 00: 00: 00.0810081 Existe: 00: 00: 00.0360036"

1M

"N'importe lequel: 00: 00: 00.0190019 Existe: 00: 00: 00.0070007"

500k

"N'importe lequel: 00: 00: 00.0120012 Existe: 00: 00: 00.0040004"

enter image description here

48
Matas Vaitkevicius

Suite à réponse de Matas à l'analyse comparative.

TL/DR : Exists () et Any () sont également rapides.

Tout d'abord: l'analyse comparative à l'aide de Stopwatch n'est pas précise ( voir la réponse de series0ne sur un sujet différent, mais similaire ), mais elle est beaucoup plus précise que DateTime.

Pour obtenir des lectures très précises, utilisez le profilage des performances. Mais une façon de comprendre comment les performances des deux méthodes se mesurent consiste à exécuter les deux méthodes charge fois, puis en comparant le temps d'exécution le plus rapide de chacun. De cette façon, peu importe que JIT et les autres bruits nous donnent de mauvaises lectures (et il fait ), car les deux exécutions sont " également égarées "dans un sens.

static void Main(string[] args)
    {
        Console.WriteLine("Generating list...");
        List<string> list = GenerateTestList(1000000);
        var s = string.Empty;

        Stopwatch sw;
        Stopwatch sw2;
        List<long> existsTimes = new List<long>();
        List<long> anyTimes = new List<long>();

        Console.WriteLine("Executing...");
        for (int j = 0; j < 1000; j++)
        {
            sw = Stopwatch.StartNew();
            if (!list.Exists(o => o == "0123456789012"))
            {
                sw.Stop();
                existsTimes.Add(sw.ElapsedTicks);
            }
        }

        for (int j = 0; j < 1000; j++)
        {
            sw2 = Stopwatch.StartNew();
            if (!list.Exists(o => o == "0123456789012"))
            {
                sw2.Stop();
                anyTimes.Add(sw2.ElapsedTicks);
            }
        }

        long existsFastest = existsTimes.Min();
        long anyFastest = anyTimes.Min();

        Console.WriteLine(string.Format("Fastest Exists() execution: {0} ticks\nFastest Any() execution: {1} ticks", existsFastest.ToString(), anyFastest.ToString()));
        Console.WriteLine("Benchmark finished. Press any key.");
        Console.ReadKey();
    }

    public static List<string> GenerateTestList(int count)
    {
        var list = new List<string>();
        for (int i = 0; i < count; i++)
        {
            Random r = new Random();
            int it = r.Next(0, 100);
            list.Add(new string('s', it));
        }
        return list;
    }

Après avoir exécuté le code ci-dessus à quatre reprises (ce qui représente à son tour 1 000 Exists() et Any() sur une liste de 1 000 000 éléments), il n’est pas difficile de voir que les méthodes sont à peu près aussi rapides.

Fastest Exists() execution: 57881 ticks
Fastest Any() execution: 58272 ticks

Fastest Exists() execution: 58133 ticks
Fastest Any() execution: 58063 ticks

Fastest Exists() execution: 58482 ticks
Fastest Any() execution: 58982 ticks

Fastest Exists() execution: 57121 ticks
Fastest Any() execution: 57317 ticks

il y a une légère différence, mais c'est une différence trop petite pour ne pas être expliquée par le bruit de fond. J'imagine que si on faisait 10 000 ou 100 000 Exists() et Any() à la place, cette légère différence disparaîtrait plus ou moins.

14
Jerri Kangasniemi

Lorsque vous corrigez les mesures - comme mentionné ci-dessus: N'importe lequel et existant, et en ajoutant la moyenne - nous obtiendrons le résultat suivant:

Executing search Exists() 1000 times ... 
Average Exists(): 35566,023
Fastest Exists() execution: 32226 

Executing search Any() 1000 times ... 
Average Any(): 58852,435
Fastest Any() execution: 52269 ticks

Benchmark finished. Press any key.
4
jasmintmp

De plus, cela ne fonctionnera que si Value est de type bool. Normalement, ceci est utilisé avec les prédicats. Tout prédicat serait généralement utilisé pour rechercher s’il existe un élément quelconque satisfaisant une condition donnée. Ici, vous ne faites qu'une carte de votre élément i à une propriété bool. Il recherchera un "i" dont la propriété Value est vraie. Une fois cela fait, la méthode retournera true.

4
flq