497 Stimmen

LINQ .Any VS .Exists - Was ist der Unterschied?

Was ist der Unterschied zwischen den folgenden Codezeilen, die LINQ on Collections verwenden?

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

und

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

Aktualisierung 1

Wenn ich zerlege .Exists es sieht so aus, als gäbe es keinen Code.

Aktualisierung 2

Weiß jemand, warum es dafür keinen Code gibt?

10 Stimmen

Wie sieht der von Ihnen kompilierte Code aus? Wie haben Sie disassembliert? ildasm? Was haben Sie erwartet zu finden, aber nicht gefunden?

487voto

Meinersbur Punkte 7661

Siehe Dokumentation

Liste.Existiert (Objektmethode - MSDN)

Ermittelt, ob die Liste(T) Elemente enthält, die die durch das angegebene Prädikat definierten Bedingungen erfüllen.

Dies gibt es seit .NET 2.0, also vor LINQ. Soll mit dem Prädikat verwendet werden delegieren aber Lambda-Ausdrücke sind abwärtskompatibel. Außerdem hat nur List dies (nicht einmal IList)

IEnumerable.Any (Erweiterungsmethode - MSDN)

Ermittelt, ob ein beliebiges Element einer Folge eine Bedingung erfüllt.

Dies ist neu in .NET 3.5 und verwendet Func(TSource, bool) als Argument, so dass dies mit Lambda-Ausdrücken und LINQ verwendet werden sollte.

Im Verhalten sind sie identisch.

232voto

JaredPar Punkte 699699

Der Unterschied besteht darin, dass Any eine Erweiterungsmethode für jede IEnumerable<T> definiert auf System.Linq.Enumerable. Es kann für jede IEnumerable<T> Instanz.

Exists scheint keine Erweiterungsmethode zu sein. Meine Vermutung ist, dass coll vom Typ List<T> . Wenn ja, ist Exists eine Instanzmethode, die sehr ähnlich wie Any funktioniert.

Kurz gesagt , die Methoden sind im Wesentlichen die gleichen. Die eine ist allgemeiner als die andere.

  • Jede hat auch eine Überladung, die keine Parameter benötigt und einfach nach jedem Element in der Aufzählung sucht.
  • Existiert hat keine solche Überlastung.

78voto

Matas Vaitkevicius Punkte 53532

TLDR; Leistungsmäßig Any scheint langsamer zu sein (wenn ich das richtig eingestellt habe, um beide Werte fast gleichzeitig auszuwerten)

        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"))
            {

            }

Testlistengenerator:

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

Mit 10M Datensätzen

" beliebig: 00:00:00.3770377 Vorhanden: 00:00:00.2490249"

Mit 5M Datensätzen

" beliebig: 00:00:00.0940094 Vorhanden: 00:00:00.1420142"

Mit 1M Datensätzen

" beliebig: 00:00:00.0180018 Vorhanden: 00:00:00.0090009"

Mit 500k, (ich habe auch die Reihenfolge umgedreht, in der sie ausgewertet werden, um zu sehen, ob es keine zusätzliche Operation gibt, die damit verbunden ist, welche davon zuerst läuft).

" Existiert: 00:00:00.0050005 beliebig: 00:00:00.0100010"

Mit 100k Datensätzen

" Existiert: 00:00:00.0010001 beliebig: 00:00:00.0020002"

Es scheint Any um eine Größenordnung von 2 langsamer sein.

Bearbeiten: Für 5 und 10M Datensätze habe ich die Art und Weise, wie die Liste generiert wird, geändert und Exists wurde plötzlich langsamer als Any was darauf hindeutet, dass mit der Art und Weise, wie ich prüfe, etwas nicht stimmt.

Neuer Prüfmechanismus:

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"))
            {

            }
        }

Bearbeiten2: Um jeglichen Einfluss der erzeugten Testdaten zu eliminieren, habe ich alles in eine Datei geschrieben und lese es nun von dort.

 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

" beliebig: 00:00:00.1640164 Vorhanden: 00:00:00.0750075"

5M

" beliebig: 00:00:00.0810081 Vorhanden: 00:00:00.0360036"

1M

" beliebig: 00:00:00.0190019 Vorhanden: 00:00:00.0070007"

500k

" beliebig: 00:00:00.0120012 Vorhanden: 00:00:00.0040004"

enter image description here

26voto

Jerri Kangasniemi Punkte 587

Als Fortsetzung von Matas' Antwort zum Benchmarking.

TL/DR : Exists() und Any() sind gleich schnell.

Zunächst einmal: Das Benchmarking mit der Stoppuhr ist nicht präzise ( siehe die Antwort von series0ne zu einem anderen, aber ähnlichen Thema ), aber sie ist viel genauer als DateTime.

Der Weg zu wirklich präzisen Messwerten führt über das Performance Profiling. Eine Möglichkeit, ein Gefühl dafür zu bekommen, wie die Leistung der beiden Methoden im Vergleich zueinander aussieht, ist die Ausführung beider Methoden lädt und vergleicht dann die schnellste Ausführungszeit von jedem. Auf diese Weise spielt es keine Rolle, dass JITing und andere Störungen zu schlechten Ergebnissen führen (und es tut ), denn beide Ausführungen sind " ebenso fehlleitend " in gewissem Sinne.

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

Nach 4-maliger Ausführung des obigen Codes (die wiederum 1 000 Exists() y Any() auf eine Liste mit 1 000 000 Elementen), ist es nicht schwer zu erkennen, dass die Methoden ziemlich gleich schnell sind.

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

Dort ist ein geringer Unterschied, der aber zu gering ist, um nicht durch Hintergrundrauschen erklärt zu werden. Meine Vermutung wäre, dass, wenn man 10 000 oder 100 000 Exists() y Any() stattdessen würde dieser geringe Unterschied mehr oder weniger verschwinden.

8voto

jasmintmp Punkte 113

Wenn Sie die Messungen korrigieren - wie oben erwähnt: Any und Exists, und die Addition des Durchschnitts - erhalten wir folgende Ausgabe:

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.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X