LINQ .Any VS .Exists - Sự khác biệt là gì?


413

Sử dụng LINQ trên các bộ sưu tập, sự khác biệt giữa các dòng mã sau đây là gì?

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

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

Cập nhật 1

Khi tôi tháo rời .Existscó vẻ như không có mã.

Cập nhật 2

Bất cứ ai cũng biết tại sao không có mã ở đây cho cái này?


9
Làm thế nào để mã bạn biên dịch trông như thế nào? Làm thế nào bạn tháo rời? ildasm? Những gì bạn đã mong đợi để tìm thấy nhưng không?
Meinersbur

Câu trả lời:


423

Xem tài liệu

List.Exists (Phương thức đối tượng - MSDN)

Xác định xem Danh sách (T) có chứa các phần tử khớp với các điều kiện được xác định bởi vị từ đã chỉ định hay không.

Điều này tồn tại kể từ .NET 2.0, vì vậy trước LINQ. Đồng nghĩa với việc được sử dụng với đại biểu Vị ngữ , nhưng các biểu thức lambda tương thích ngược. Ngoài ra, chỉ có List có cái này (thậm chí không phải IList)

IEnumerable.Any (Phương thức mở rộng - MSDN)

Xác định xem bất kỳ phần tử nào của chuỗi thỏa mãn một điều kiện.

Đây là tính năng mới trong .NET 3.5 và sử dụng Func (TSource, bool) làm đối số, do đó, mục đích này được sử dụng với các biểu thức lambda và LINQ.

Trong hành vi, đây là giống hệt nhau.


4
Sau đó tôi đã tạo một bài đăng trong một chủ đề khác , nơi tôi liệt kê tất cả các "tương đương" Linq của các List<>phương thức ví dụ .NET 2 .
Jeppe Stig Nielsen

201

Sự khác biệt là Any là một phương thức mở rộng cho bất kỳ IEnumerable<T>định nghĩa nào trên System.Linq.Enumerable. Nó có thể được sử dụng trong mọi IEnumerable<T>trường hợp.

Tồn tại dường như không phải là một phương pháp mở rộng. Tôi đoán là coll là loại List<T>. Nếu vậy Exists là một phương thức thể hiện có chức năng rất giống với Any.

Tóm lại , các phương pháp về cơ bản là giống nhau. Một cái là tổng quát hơn cái kia.

  • Bất kỳ cũng có một quá tải mà không có tham số và chỉ đơn giản là tìm kiếm bất kỳ mục nào trong vô số.
  • Tồn tại không có quá tải như vậy.

13
Đặt tốt (+1). Danh sách <T> .Exists đã xuất hiện từ .Net 2 nhưng chỉ hoạt động cho các danh sách chung. IEnumerable <T> .Any đã được thêm vào .Net 3 dưới dạng tiện ích mở rộng hoạt động trên mọi bộ sưu tập có thể đếm được. Cũng có các thành viên tương tự như Danh sách <T> .Count, là một thuộc tính và IEnumerable <T> .Count () - một phương thức.
Keith

51

TLDR; Hiệu suất-khôn ngoan Anydường như chậm hơn (nếu tôi đã thiết lập đúng cách này để đánh giá cả hai giá trị gần như cùng một lúc)

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

            }

kiểm tra danh sách tạo:

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

Với hồ sơ 10 triệu

"Bất kỳ: 00: 00: 00.3770377 Tồn tại: 00: 00: 00.2490249"

Với hồ sơ 5 triệu

"Bất kỳ: 00: 00: 00.0940094 Tồn tại: 00: 00: 00.1420142"

Với hồ sơ 1 triệu

"Bất kỳ: 00: 00: 00.0180018 Tồn tại: 00: 00: 00.0090009"

Với 500k, (tôi cũng đã lật lại thứ tự mà họ được đánh giá để xem liệu không có hoạt động bổ sung nào liên quan đến việc nào chạy trước.)

"Tồn tại: 00: 00: 00.0050005 Bất kỳ: 00: 00: 00.0100010"

Với hồ sơ 100k

"Tồn tại: 00: 00: 00.0010001 Bất kỳ: 00: 00: 00.0020002"

Nó dường như Anychậm hơn bởi cường độ 2.

Chỉnh sửa: Đối với các bản ghi 5 và 10 triệu, tôi đã thay đổi cách tạo danh sách và Existsđột nhiên trở nên chậm hơn so với Anyngụ ý rằng có gì đó không đúng trong cách tôi đang kiểm tra.

Cơ chế thử nghiệm mới:

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 để loại bỏ mọi ảnh hưởng từ việc tạo dữ liệu thử nghiệm, tôi đã viết tất cả vào tệp và bây giờ đọc nó từ đó.

 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 triệu

"Bất kỳ: 00: 00: 00.1640164 Tồn tại: 00: 00: 00.0750075"

5 triệu

"Bất kỳ: 00: 00: 00.0810081 Tồn tại: 00: 00: 00.0360036"

1 triệu

"Bất kỳ: 00: 00: 00.0190019 Tồn tại: 00: 00: 00.0070007"

500k

"Bất kỳ: 00: 00: 00.0120012 Tồn tại: 00: 00: 00.0040004"

nhập mô tả hình ảnh ở đây


3
Không làm bạn mất uy tín, nhưng tôi cảm thấy hoài nghi về những điểm chuẩn này. Nhìn vào các con số: Mọi kết quả đều có đệ quy xảy ra (3770377: 2490249). Ít nhất là đối với tôi, đó là một dấu hiệu chắc chắn có gì đó không đúng. Tôi không chắc chắn một trăm phần trăm về toán học ở đây, nhưng tôi nghĩ rằng tỷ lệ của mô hình định kỳ đó xảy ra là 1 trên 999 ^ 999 (hoặc 999! Có thể?) Trên mỗi giá trị. Vì vậy, cơ hội của nó xảy ra 8 lần liên tiếp là vô cùng lớn. Tôi nghĩ đó là vì bạn sử dụng DateTime để đo điểm chuẩn .
Jerri Kangasniemi

@JerriKangasniemi Lặp lại cùng một thao tác trong sự cô lập sẽ luôn mất cùng một lượng thời gian, tương tự như vậy để lặp lại nó nhiều lần. Điều gì khiến bạn nói rằng đó là DateTime?
Matas Vaitkevicius

Tất nhiên là thế. Vấn đề vẫn là rất khó có thể lấy ví dụ 0120012 giây cho các cuộc gọi 500k. Và nếu nó là tuyến tính hoàn hảo, do đó giải thích các con số rất độc đáo, các cuộc gọi 1M sẽ mất 0240024 giây (dài gấp đôi), tuy nhiên không phải vậy. Cuộc gọi 1M mất 58, (3)% dài hơn 500k và 10M mất 102,5% lâu hơn 5 triệu. Vì vậy, nó không phải là một hàm tuyến tính và do đó không thực sự hợp lý cho các số cho tất cả lặp lại. Tôi đã đề cập đến DateTime vì trước đây tôi đã gặp phải sự cố với nó, vì DateTime không sử dụng bộ định thời có độ chính xác cao.
Jerri Kangasniemi

2
@JerriKangasniemi Tôi có thể đề nghị bạn sửa nó và đăng câu trả lời
Matas Vaitkevicius

1
Nếu tôi đang đọc kết quả của bạn một cách chính xác, Bạn đã báo cáo Bất kỳ chỉ bằng khoảng 2 đến 3 lần tốc độ của Tồn tại. Tôi không thấy cách dữ liệu thậm chí còn hỗ trợ một cách nhẹ nhàng cho khẳng định của bạn rằng "Dường như mọi thứ sẽ chậm hơn ở cường độ 2". Nó chậm hơn một chút, chắc chắn, không phải là thứ tự cường độ.
Suncat2000

16

Như một sự tiếp nối về câu trả lời của Matas về điểm chuẩn.

TL / DR : Tồn tại () và Any () đều nhanh như nhau.

Trước hết: Điểm chuẩn bằng Đồng hồ bấm giờ không chính xác ( xem câu trả lời của sê-ri về một chủ đề khác, nhưng tương tự ), nhưng nó chính xác hơn nhiều so với DateTime.

Cách để có được bài đọc thực sự chính xác là sử dụng Hồ sơ hiệu suất. Nhưng một cách để hiểu được cách hiệu suất của hai phương pháp đối với nhau là bằng cách thực hiện cả hai phương thức tải thời gian và sau đó so sánh thời gian thực hiện nhanh nhất của mỗi phương thức. Theo cách đó, thực sự không có vấn đề gì khi JITing và tiếng ồn khác mang lại cho chúng ta những bài đọc tồi (và nó cũng vậy ), bởi vì cả hai vụ hành quyết đều " sai lầm như nhau " theo một nghĩa nào đó.

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

Sau khi thực hiện mã trên 4 lần (lần lượt thực hiện 1 000 Exists()Any()trong danh sách có 1 000 000 phần tử), không khó để thấy rằng các phương thức này cũng nhanh không kém.

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

Có một sự khác biệt nhỏ, nhưng đó một sự khác biệt quá nhỏ để không được giải thích bằng tiếng ồn nền. Tôi đoán là nếu một người làm 10 000 hoặc 100 000 Exists()Any()thay vào đó, sự khác biệt nhỏ đó sẽ biến mất ít nhiều.


Tôi có thể đề nghị bạn thực hiện 10 000 và 100 000 và 1000000, chỉ để có phương pháp về vấn đề này, còn tại sao giá trị tối thiểu và không trung bình?
Matas Vaitkevicius

2
Giá trị tối thiểu là vì tôi muốn so sánh việc thực hiện nhanh nhất (= có lẽ ít nhất là nhiễu nền) của mỗi phương thức. Tôi có thể làm điều đó với nhiều lần lặp hơn, mặc dù sẽ muộn hơn (tôi nghi ngờ ông chủ của tôi muốn trả tiền cho tôi để làm việc này thay vì làm việc thông qua hồ sơ tồn đọng của chúng tôi)
Jerri Kangasniemi

Tôi đã yêu cầu Paul Lindberg và anh nói là nó ok;) liên quan đến ít nhất tôi có thể thấy phương pháp lập luận của bạn tuy nhiên chính thống hơn là sử dụng trung bình en.wikipedia.org/wiki/Algorithmic_efficiency#Practice
Matas Vaitkevicius

9
Nếu mã bạn đã đăng là mã bạn thực sự thực thi, không có gì đáng ngạc nhiên khi bạn nhận được kết quả tương tự, khi bạn gọi Exist trong cả hai phép đo. ;)
Simon Touchtech

Heh, yeah, tôi đã thấy điều đó bây giờ bạn nói nó. Không phải trong thực hiện của tôi mặc dù. Điều này chỉ có ý nghĩa như một khái niệm tước bỏ những gì tôi đang so sánh. : P
Jerri Kangasniemi

4

Ngoài ra, điều này sẽ chỉ hoạt động nếu Giá trị thuộc loại bool. Thông thường điều này được sử dụng với các vị ngữ. Bất kỳ vị từ nào thường được sử dụng để tìm xem có phần tử nào thỏa mãn một điều kiện cho trước không. Ở đây bạn chỉ đang thực hiện một bản đồ từ phần tử i của bạn đến một thuộc tính bool. Nó sẽ tìm kiếm một "i" có thuộc tính Giá trị là đúng. Sau khi thực hiện, phương thức sẽ trả về true.


3

Khi bạn sửa các phép đo - như đã đề cập ở trên: Bất kỳ và Tồn tại và thêm trung bình - chúng tôi sẽ nhận được kết quả sau:

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.
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.