LINQ .Any VS. Var - Fark nedir?


413

Koleksiyonlarda LINQ kullanarak aşağıdaki kod satırları arasındaki fark nedir?

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

ve

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

Güncelleme 1

Söktüğümde .Existskod yok gibi görünüyor.

Güncelleme 2

Bunun için neden kod olmadığını bilen var mı?


9
Derlediğiniz kod nasıl görünüyor? Nasıl söktün? ildasm? Ne bulmayı bekledin, ama etmedin?
Meinersbur

Yanıtlar:


423

Belgelere bakın

List.Exists (Nesne yöntemi - MSDN)

Listenin (T) belirtilen yüklem tarafından tanımlanan koşullara uyan öğeler içerip içermediğini belirler.

Bu .NET 2.0 beri var, bu yüzden LINQ önce. Predicate delegesi ile kullanılması gerekiyordu , ancak lambda ifadeleri geriye dönük olarak uyumlu. Ayrıca, sadece List'de bu var (IList bile değil)

IEnumerable.Any (Uzantı yöntemi - MSDN)

Bir dizideki herhangi bir öğenin bir koşulu karşılayıp karşılamadığını belirler.

Bu .NET 3.5'te yenidir ve bağımsız değişken olarak Func (TSource, bool) kullanır, bu nedenle bu lambda ifadeleri ve LINQ ile kullanılmak üzere tasarlanmıştır.

Davranışta, bunlar aynıdır.


4
Daha sonra başka bir iş parçacığında bir yazı yaptım burada tüm Linq "eşdeğerlerini" .NET 2 List<>örnek yöntemleri listeledim .
Jeppe Stig Nielsen

201

Aradaki fark, Any öğesinin IEnumerable<T>System.Linq.Enumerable üzerinde tanımlanan herhangi bir uzantı yöntemi olmasıdır . Herhangi bir yerde kullanılabilirIEnumerable<T> durumda .

Var olan bir uzatma yöntemi gibi görünmüyor. Benim tahminime göre coll tiptir List<T>. Öyleyse, Var, Herhangi birine çok benzer bir örnek yöntemdir.

Kısacası , yöntemler esasen aynıdır. Biri diğerinden daha geneldir.

  • Herhangi bir parametre almaz ve numaralandırılabilir herhangi bir öğe arar aşırı yük vardır.
  • Var olanların böyle bir aşırı yüklemesi yoktur.

13
İyi koy (+1). Liste <T> .Exists, .Net 2'den beri var olmakla birlikte, yalnızca genel listelerde çalışır. IEnumerable <T> .Any, herhangi bir numaralandırılabilir koleksiyonda çalışan bir uzantı olarak .Net 3'e eklenmiştir. Bir özellik olan List <T> .Count ve bir yöntem olan IEnumerable <T> .Count () - gibi benzer üyeler de vardır.
Keith

51

TLDR; Performans açısından Anydaha yavaş görünüyor (bunu her iki değeri de neredeyse aynı anda değerlendirmek için doğru şekilde ayarladıysam)

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

            }

test listesi üreteci:

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

10 milyon kayıt ile

"Herhangi biri: 00: 00: 00.3770377 Var: 00: 00: 00.2490249"

5 milyon kayıt ile

"Herhangi biri: 00: 00: 00.0940094 Var: 00: 00: 00.1420142"

1 milyon kayıt ile

"Herhangi biri: 00: 00: 00.0180018 Var: 00: 00: 00.0090009"

500k ile, (Ayrıca hangisi önce çalışırsa ilişkili başka bir işlem olup olmadığını görmek için değerlendirildikleri sıra etrafında çevirdim.)

"Var: 00: 00: 00.0050005 Herhangi biri: 00: 00: 00.0100010"

100 bin kayıt ile

"Var: 00: 00: 00.0010001 Herhangi biri: 00: 00: 00.0020002"

Any2 büyüklüğüne göre daha yavaş görünüyor .

Düzenleme: 5 ve 10M kayıtları için ben liste oluşturur şekilde değişti ve Existsaniden Anytest yolunda yanlış bir şey olduğunu ima daha yavaş oldu .

Yeni test mekanizması:

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: Tamam böylece test verileri oluşturmak herhangi bir etkisi ortadan kaldırmak için hepsini dosyaya yazdım ve şimdi oradan okuyun.

 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

"Herhangi biri: 00: 00: 00.1640164 Var: 00: 00: 00.0750075"

5M

"Herhangi biri: 00: 00: 00.0810081 Var: 00: 00: 00.0360036"

1M

"Herhangi biri: 00: 00: 00.0190019 Var: 00: 00: 00.0070007"

500k

"Herhangi biri: 00: 00: 00.0120012 Var: 00: 00: 00.0040004"

resim açıklamasını buraya girin


3
Sana saygısızlık yok, ama bu kriterler konusunda şüpheci hissediyorum. Sayılara bakın: Her sonucun bir tekrarlaması olur (3770377: 2490249). En azından benim için, bu bir şeyin doğru olmadığına dair kesin bir işaret. Buradaki matematikte yüzde yüz emin değilim, ama yinelenen paternin gerçekleşme olasılığının değer başına 999 ^ 999 (veya 999! Belki?) 1 olduğunu düşünüyorum. Yani arka arkaya 8 kez olma şansı sonsuzdur. Ben kıyaslama için DateTime kullandığınız için düşünüyorum .
Jerri Kangasniemi

@JerriKangasniemi Aynı işlemi tek başına tekrarlamak her zaman aynı miktarda zaman almalıdır, aynı işlemi birden çok kez tekrarlamak için de geçerlidir. Bunun DateTime olduğunu söyleten nedir?
Matas Vaitkevicius

Tabii ki öyle. Sorun hala 500k çağrılar için örneğin 0120012 saniye sürmesi pek olası değildir. Ve eğer mükemmel derecede doğrusal olsaydı, bu yüzden sayıları çok güzel açıklarsa, 1M çağrıları 0240024 saniye (iki kat daha uzun) alacaktı, ancak durum böyle değil. 1M çağrıları 58, (3)% 500k'den daha uzun ve 10M çağrıları 5M'den% 102,5 daha uzun sürer. Yani bu doğrusal bir işlev değildir ve bu nedenle sayılar için tüm kurallara uygun değildir. DateTime'dan bahsetmiştim, çünkü DateTime'ın yüksek hassasiyetli zamanlayıcılar kullanmadığı için geçmişte kendimle ilgili sorunlar yaşadım.
Jerri Kangasniemi

2
@JerriKangasniemi Bunu düzeltmenizi ve bir cevap göndermenizi önerebilir miyim
Matas Vaitkevicius

1
Sonuçlarınızı doğru şekilde okuyorsam, Any (Varolan) hızının yalnızca 2 ila 3 katı olduğunu bildirmişsinizdir. Verilerin, "2 büyüklüğüne göre daha yavaş olması herhangi biri gibi görünebilir" iddianızı nasıl hafifçe desteklediğini görmüyorum. Biraz daha yavaş, elbette, büyüklük emirleri değil.
Suncat2000

16

Matas'ın cevabının devamı olarak kıyaslama konusundaki .

TL / DR : Var () ve Any () eşit derecede hızlı.

İlk önce: Kronometreyi kullanarak karşılaştırma yapmak kesin değil ( bkz. Series0ne'ın farklı, ancak benzer bir konuda cevabı ), ancak DateTime'dan çok daha .

Gerçekten hassas okumalar elde etmenin yolu Performans Profili Oluşturma kullanmaktır. Ama tek yön her iki yöntemleri yürüterek iki yöntemin, performans birbirimizi kadar ölçmek nasıl bir fikir edinmek için yükleri kez ve sonra her birinin en hızlı yürütme zamanı karşılaştırılması. Bu şekilde, gerçekten JITing ve diğer gürültü bize kötü okumaları verir (ve o önemli değil does her iki infazlar "çünkü,) eşit misguiding bir anlamda".

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

Yukarıdaki kodu 4 kez yürüttükten sonra (sırayla 1000 Exists()ve Any()1000 000 elemanlı bir listede), yöntemlerin eşit derecede hızlı olduğunu görmek zor değildir.

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

Orada olan çok az fark, ancak bu arka plan gürültüsünü açıklanamaz çok küçük bir fark var. Tahminimce kişi 10 000 veya 100 000 yapsaydı Exists()ve Any()bunun yerine, bu ufak fark az ya da çok ortadan kalkacaktır.


Bu konuda metodik olmak için 10 000 ve 100 000 ve 1000000 yapmanızı önerebilir miyim, neden ortalama değer değil min.
Matas Vaitkevicius

2
Minimum değer, her yöntemin en hızlı yürütme (= muhtemelen en az arka plan gürültü miktarı) karşılaştırmak istiyorum çünkü. Daha fazla yineleme ile yapabilirim, ancak daha sonra olacak (patronum iş birikimimizle çalışmak yerine bunu yapmak için bana ödeme yapmak istediğinden şüpheliyim)
Jerri Kangasniemi

Paul Lindberg'e sordum ve tamam olduğunu söyledi;) minimum olarak, mantığınızı görebiliyorum, ancak daha ortodoks yaklaşım ortalama en.wikipedia.org/wiki/Algorithmic_efficiency#Practice
Matas Vaitkevicius

9
Gönderdiğiniz kod gerçekten yürüttüğünüz kodsa, her iki ölçümde de Mevcut olarak çağırdığınızda benzer sonuçlar almanız şaşırtıcı değildir. ;)
Simon Touchtech

Heh, evet, şimdi ben de söyledin. Ama idamımda değil. Bu sadece karşılaştırdıklarımın soyulmuş bir konsepti anlamına geliyordu. : P
Jerri Kangasniemi

4

Ayrıca, bu yalnızca Değer bool türünde olduğunda çalışır. Normalde bu tahminlerle birlikte kullanılır. Herhangi bir yüklem genellikle, belirli bir koşulu karşılayan herhangi bir unsurun bulunup bulunmadığını bulmak için kullanılır. Burada sadece i öğenizden bir bool özelliğine bir harita yapıyorsunuz. Value özelliği true olan bir "i" arar. Tamamlandığında, yöntem true değerini döndürecektir.


3

Ölçümleri düzeltirseniz - yukarıda belirtildiği gibi: Herhangi biri ve Var ve ortalama ekleyerek - aşağıdaki çıktıyı alırız:

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.
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.