it-swarm.com.de

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

Was ist der Unterschied zwischen den folgenden Codezeilen, wenn Sie Linq für Sammlungen verwenden?

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

und

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

pdate 1

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

pdate 2

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

385
Anthony D

Siehe Dokumentation

List.Exists (Objektmethode - MSDN)

Bestimmt, ob die Liste (T) Elemente enthält, die den durch das angegebene Prädikat definierten Bedingungen entsprechen.

Dies existiert seit .NET 2.0, also vor LINQ. Geeignet für die Verwendung mit dem Predicate-Delegaten , aber Lambda-Ausdrücke sind abwärtskompatibel. Auch nur List hat dies (nicht einmal IList)

IEnumerable.Any (Erweiterungsmethode - MSDN)

Bestimmt, ob ein Element einer Sequenz 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 diese identisch.

394
Meinersbur

Der Unterschied besteht darin, dass Any eine Erweiterungsmethode für IEnumerable<T> Ist, die in System.Linq.Enumerable definiert ist. Es kann für jede Instanz IEnumerable<T> Verwendet werden.

Exists scheint keine Erweiterungsmethode zu sein. Ich vermute, dass coll vom Typ List<T> Ist. In diesem Fall ist Exists eine Instanzmethode, die Any sehr ähnlich ist.

Kurz gesagt, die Methoden sind im Wesentlichen gleich. Einer ist allgemeiner als der andere.

  • Any hat auch eine Überladung, die keine Parameter annimmt und einfach nach einem Element in der Aufzählung sucht.
  • Exists hat keine solche Überlastung.
190
JaredPar

TLDR; Leistungsmäßig scheint Any langsamer zu sein (wenn ich dies 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 10 Millionen Datensätzen

Beliebig: 00: 00: 00.3770377 vorhanden: 00: 00: 00.2490249

Mit 5 Millionen 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 gewechselt, in der sie ausgewertet werden, um festzustellen, ob es keine zusätzliche Operation gibt, die mit der zuerst ausgeführten Operation verknüpft ist.)

Vorhanden: 00: 00: 00.0050005 Beliebig: 00: 00: 00.0100010

Mit 100.000 Datensätzen

Vorhanden: 00: 00: 00.0010001 Beliebig: 00: 00: 00.0020002

Es scheint, dass Any um die Größe 2 langsamer ist.

Bearbeiten: Für 5 und 10 Millionen Datensätze habe ich die Art und Weise geändert, in der die Liste erstellt wird, und Exists wurde plötzlich langsamer als Any Das bedeutet, dass etwas in der Art, wie ich teste, nicht stimmt.

Neuer Testmechanismus:

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 um jeglichen Einfluss bei der Generierung von Testdaten auszuschließen, habe ich alles in eine Datei geschrieben und jetzt gelesen 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

48

Als Fortsetzung zu 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 es ist weitaus präziser als DateTime.

Die Möglichkeit, wirklich präzise Messwerte zu erhalten, besteht in der Verwendung von Leistungsprofilen. Ein Weg, um ein Gefühl dafür zu bekommen, wie die Leistung der beiden Methoden zueinander passt, besteht darin, beide Methoden auszuführen Laden von Zeiten und dann die schnellste Ausführungszeit von jedem zu vergleichen. Auf diese Weise spielt es keine Rolle, dass JITing und anderes Rauschen zu schlechten Messwerten führen (und zwar tut es), da beide Ausführungen " gleichermaßen fehlgeleitet" sind. in einem Sinn.

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 viermaliger Ausführung des obigen Codes (der wiederum 1 000 Exists() und Any() für eine Liste mit 1 000 000 Elementen ausführt) ist es nicht schwer zu erkennen, dass die Methoden ziemlich ähnlich sind genauso schnell.

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

Es gibt is einen kleinen Unterschied, aber es ist zu klein, um nicht durch Hintergrundgeräusche erklärt zu werden. Ich schätze, wenn man stattdessen 10 000 oder 100 000 Exists() und Any() macht, verschwindet dieser kleine Unterschied mehr oder weniger.

14

Wenn Sie die Messungen korrigieren - wie oben erwähnt: Beliebig und vorhanden, und den Durchschnitt addieren - erhalten Sie 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.
4
jasmintmp

Dies funktioniert nur, wenn Value vom Typ bool ist. Normalerweise wird dies mit Prädikaten verwendet. Im Allgemeinen wird jedes Prädikat verwendet, um herauszufinden, ob ein Element eine bestimmte Bedingung erfüllt. Hier machst du nur eine Map von deinem Element i zu einer bool-Eigenschaft. Es wird nach einem "i" gesucht, dessen Value-Eigenschaft wahr ist. Sobald dies erledigt ist, gibt die Methode true zurück.

4
flq