LINQ .Setiap VS. Ada - Apa bedanya?

413

Menggunakan LINQ pada koleksi, apa perbedaan antara baris kode berikut?

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

dan

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

Perbarui 1

Ketika saya membongkar .Existssepertinya tidak ada kode.

Perbarui 2

Adakah yang tahu mengapa tidak ada kode untuk kode ini?

Anthony D
sumber
9
Seperti apa bentuk kode yang Anda kompilasi? Bagaimana Anda membongkar? ildasm? Apa yang Anda harapkan tetapi tidak ditemukan?
Meinersbur

Jawaban:

423

Lihat dokumentasi

List.Exists (Metode objek - MSDN)

Menentukan apakah Daftar (T) berisi elemen yang cocok dengan kondisi yang ditentukan oleh predikat yang ditentukan.

Ini ada sejak .NET 2.0, jadi sebelum LINQ. Dimaksudkan untuk digunakan dengan delegasi Predikat , tetapi ekspresi lambda kompatibel ke belakang. Juga, hanya Daftar yang memiliki ini (bahkan bukan IList)

IEnumerable.Any (Metode ekstensi - MSDN)

Menentukan apakah elemen dari urutan memenuhi syarat.

Ini baru dalam. NET 3.5 dan menggunakan Func (TSource, bool) sebagai argumen, jadi ini dimaksudkan untuk digunakan dengan ekspresi lambda dan LINQ.

Dalam perilaku, ini identik.

Meinersbur
sumber
4
Saya kemudian membuat posting di utas lain di mana saya mencantumkan semua Linq "setara" dari List<>metode contoh .NET 2 .
Jeppe Stig Nielsen
201

Perbedaannya adalah bahwa Any adalah metode ekstensi untuk semua yang IEnumerable<T>didefinisikan pada System.Linq.Enumerable. Itu dapat digunakan pada IEnumerable<T>contoh apa saja .

Tampaknya tidak ada metode ekstensi. Dugaan saya adalah coll adalah tipe List<T>. If so Exists adalah metode instance yang fungsinya sangat mirip dengan Any.

Singkatnya , metode dasarnya sama. Yang satu lebih umum daripada yang lain.

  • Any juga memiliki kelebihan yang tidak memerlukan parameter dan hanya mencari item apa saja di enumerable.
  • Ada tidak memiliki kelebihan seperti itu.
JaredPar
sumber
13
Baik menempatkan (+1). Daftar <T> .Ada sudah ada sejak .Net 2 tetapi hanya berfungsi untuk daftar umum. IEnumerable <T> .Any ditambahkan dalam .Net 3 sebagai ekstensi yang berfungsi pada koleksi enumerable apa pun. Ada juga anggota yang mirip seperti List <T> .Count, yang merupakan properti dan IEnumerable <T> .Count () - sebuah metode.
Keith
51

TLDR; Kinerja-bijaksana Anytampaknya lebih lambat (jika saya sudah mengatur ini dengan benar untuk mengevaluasi kedua nilai pada waktu yang hampir bersamaan)

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

            }

daftar pengujian generator:

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

Dengan catatan 10 juta

"Setiap: 00: 00: 00.3770377 Ada: 00: 00: 00.2490249"

Dengan catatan 5 juta

"Apa pun: 00: 00: 00.0940094 Ada: 00: 00: 00.1420142"

Dengan catatan 1 juta

"Setiap: 00: 00: 00.0180018 Ada: 00: 00: 00.0090009"

Dengan 500k, (Saya juga membalik urutan agar mereka dievaluasi untuk melihat apakah tidak ada operasi tambahan yang terkait dengan mana yang berjalan terlebih dahulu.)

"Ada: 00: 00: 00.0050005 Apa saja: 00: 00: 00.0100010"

Dengan catatan 100 ribu

"Ada: 00: 00: 00.0010001 Apa saja: 00: 00: 00.0020002"

Tampaknya Anyakan lebih lambat dengan besarnya 2.

Sunting: Untuk catatan 5 dan 10M saya mengubah cara menghasilkan daftar dan Existstiba - tiba menjadi lebih lambat daripada Anyyang menyiratkan ada sesuatu yang salah dalam cara saya menguji.

Mekanisme pengujian baru:

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

            }
        }

Sunting2: Ok jadi untuk menghilangkan pengaruh dari menghasilkan data uji, saya menulis semuanya ke file dan sekarang membacanya dari sana.

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

10 jt

"Setiap: 00: 00: 00.1640164 Ada: 00: 00: 00.0750075"

5 jt

"Setiap: 00: 00: 00.0810081 Ada: 00: 00: 00.0360036"

1 jt

"Setiap: 00: 00: 00.0190019 Ada: 00: 00: 00.0070007"

500rb

"Setiap: 00: 00: 00.0120012 Ada: 00: 00: 00.0040004"

masukkan deskripsi gambar di sini

Matas Vaitkevicius
sumber
3
Tidak mendiskreditkan Anda, tetapi saya merasa skeptis dengan tolok ukur ini. Lihatlah angka-angkanya: Setiap hasil mengalami rekursi (3770377: 2490249). Setidaknya bagi saya, itu pertanda pasti ada sesuatu yang tidak benar. Saya tidak seratus persen yakin dengan matematika di sini, tapi saya pikir peluang pola berulang itu terjadi adalah 1 banding 999 ^ 999 (atau 999! Mungkin?) Per nilai. Jadi kesempatan itu terjadi 8 kali berturut-turut sangat kecil. Saya pikir itu karena Anda menggunakan DateTime untuk pembandingan .
Jerri Kangasniemi
@JerriKangasniemi Mengulangi operasi yang sama secara terpisah harus selalu mengambil jumlah waktu yang sama, hal yang sama berlaku untuk mengulanginya berkali-kali. Apa yang membuat Anda mengatakan itu adalah DateTime?
Matas Vaitkevicius
Tentu saja. Masalahnya adalah masih sangat tidak mungkin untuk mengambil misalnya 0120012 detik untuk panggilan 500k. Dan jika itu akan menjadi linear sempurna, sehingga menjelaskan angka-angka dengan sangat baik, panggilan 1M akan membutuhkan waktu 0240024 detik (dua kali lebih lama), namun itu tidak terjadi. Panggilan 1M membutuhkan 58, (3)% lebih lama dari 500k dan 10M membutuhkan 102,5% lebih lama dari 5M. Jadi itu bukan fungsi linier dan dengan demikian tidak masuk akal untuk angka-angka untuk semua kambuh. Saya menyebutkan DateTime karena saya sendiri pernah mengalami masalah dengannya, karena DateTime tidak menggunakan timer presisi tinggi.
Jerri Kangasniemi
2
@JerriKangasniemi Bisakah saya menyarankan Anda memperbaikinya dan mengirim jawaban
Matas Vaitkevicius
1
Jika saya membaca hasil Anda dengan benar, Anda melaporkan Any hanya sekitar 2 hingga 3 kali kecepatan yang ada. Saya tidak melihat bagaimana data bahkan sedikit mendukung pernyataan Anda bahwa "Tampaknya Any lebih lambat dengan besarnya 2". Ini sedikit lebih lambat, tentu saja, bukan urutan besarnya.
Suncat2000
16

Sebagai kelanjutan dari jawaban Matas pada benchmarking.

TL / DR : Ada () dan Any () sama-sama cepat.

Pertama: Pembandingan menggunakan Stopwatch tidak tepat ( lihat jawaban series0ne pada topik yang berbeda namun serupa ), tetapi jauh lebih tepat daripada DateTime.

Cara untuk mendapatkan pembacaan yang sangat tepat adalah dengan menggunakan Performance Profiling. Tapi salah satu cara untuk mendapatkan rasa bagaimana kinerja dua metode mengukur sampai satu sama yaitu lain dengan menjalankan kedua metode beban kali dan kemudian membandingkan waktu eksekusi tercepat masing-masing. Dengan begitu, benar-benar tidak masalah bahwa JITing dan kebisingan lainnya memberi kita bacaan yang buruk (dan memang demikian ), karena kedua eksekusi itu " sama-sama menyesatkan " dalam arti tertentu.

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

Setelah mengeksekusi kode di atas 4 kali (yang pada gilirannya menghasilkan 1 000 Exists()dan Any()pada daftar dengan 1 000 elemen), tidak sulit untuk melihat bahwa metode ini cukup cepat.

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

Ada adalah sedikit perbedaan, tetapi perbedaan terlalu kecil untuk tidak dijelaskan oleh kebisingan latar belakang. Dugaan saya adalah bahwa jika seseorang melakukan 10.000 atau 100.000 Exists()dan Any()sebagai gantinya, sedikit perbedaan akan hilang lebih atau kurang.

Jerri Kangasniemi
sumber
Bisakah saya menyarankan Anda melakukan 10.000 dan 100.000 dan 10.000, hanya untuk menjadi metodis tentang ini, juga mengapa min dan bukan nilai rata-rata?
Matas Vaitkevicius
2
Nilai minimum adalah karena saya ingin membandingkan eksekusi tercepat (= mungkin paling sedikit noise latar belakang) dari setiap metode. Saya mungkin melakukannya dengan lebih banyak iterasi, meskipun itu nanti (saya ragu bos saya ingin membayar saya untuk melakukan ini alih-alih mengerjakan backlog kami)
Jerri Kangasniemi
Saya telah bertanya kepada Paul Lindberg dan dia bilang tidak apa-apa;) dalam hal minimum saya bisa melihat alasan Anda namun pendekatan yang lebih ortodoks adalah dengan menggunakan rata-rata en.wikipedia.org/wiki/Algorithmic_efficiency#Practice
Matas Vaitkevicius
9
Jika kode yang Anda posting adalah kode yang benar-benar Anda jalankan, tidak mengherankan bahwa Anda mendapatkan hasil yang sama, saat Anda memanggil Ada di kedua pengukuran. ;)
Simon Touchtech
Heh, ya, aku melihat itu juga sekarang kamu mengatakannya. Namun tidak dalam eksekusi saya. Ini hanya dimaksudkan sebagai konsep sederhana dari apa yang saya bandingkan. : P
Jerri Kangasniemi
4

Selain itu, ini hanya akan berfungsi jika Nilai adalah tipe bool. Biasanya ini digunakan dengan predikat. Predikat apa pun biasanya digunakan untuk mencari apakah ada unsur yang memenuhi syarat tertentu. Di sini Anda hanya melakukan peta dari elemen i ke properti bool. Ini akan mencari "i" yang nilai properti-nya benar. Setelah selesai, metode ini akan mengembalikan true.

flq
sumber
3

Ketika Anda memperbaiki pengukuran - seperti yang disebutkan di atas: Apa saja dan Ada, dan menambahkan rata-rata - kami akan mendapatkan hasil sebagai berikut:

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.
jasmintmp
sumber