Linq. Любой VS.Exists - Какая разница?

Используя Linq в коллекциях, в чем разница между следующими строками кода?

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

и

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

Обновление 1

Когда я разбираю .Exists, похоже, что нет кода.

Обновление 2

Кто-нибудь знает, почему для этого нет кода?

Ответ 1

См. документацию

List.Exists (Метод объекта - MSDN)

Определяет, содержит ли List (T) элементы, которые соответствуют условиям, определенным указанным предикатом.

Это существует с .NET 2.0, поэтому перед LINQ. Предназначен для использования с делегатом , но лямбда-выражения обратно совместимы. Кроме того, только List имеет это (даже не IList)

IEnumerable.Any (метод расширения - MSDN)

Определяет, удовлетворяет ли любому элементу последовательности условие.

Это новое в .NET 3.5 и использует аргумент Func (TSource, bool), поэтому он предназначен для использования с лямбда-выражениями и LINQ.

В поведении они идентичны.

Ответ 2

Разница в том, что Any является методом расширения для любого IEnumerable<T>, определенного в System.Linq.Enumerable. Его можно использовать для любого экземпляра IEnumerable<T>.

Exists не является методом расширения. Я предполагаю, что coll имеет тип List<T>. Если так, Exists - это метод экземпляра, который очень похож на Any.

Короче, методы по существу одинаковы. Один из них более общий, чем другой.

  • Любой также имеет перегрузку, которая не принимает параметров и просто ищет любой элемент в перечисляемом.
  • Существует не имеет такой перегрузки.

Ответ 3

TL;DR; Производительность Any кажется медленнее (если я правильно настроил это для оценки обоих значений почти в одно и то же время)

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

            }

генератор списка тестов:

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

С записями 10M

"Любое: 00: 00: 00.3770377 Есть: 00: 00: 00.2490249"

С записями 5M

"Любое: 00: 00: 00.0940094 Есть: 00: 00: 00.1420142"

С записями 1M

"Любое: 00: 00: 00.0180018 Есть: 00: 00: 00.0090009"

С 500k (я также перевернул порядок, в котором они получают оценку, чтобы увидеть, нет ли дополнительной операции, связанной с тем, что будет выполнено в первую очередь.)

"Существует: 00: 00: 00.0050005 Любое: 00: 00: 00.0100010"

С 100 тыс. записей

"Существует: 00: 00: 00.0010001 Любой: 00: 00: 00.0020002"

Казалось бы, Any будет медленнее по величине 2.

Изменить: Для 5 и 10 М записей я изменил способ генерации списка, а Exists внезапно стал медленнее, чем Any, что подразумевает что-то не так в том, как я тестирую.

Новый механизм тестирования:

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: Хорошо, поэтому, чтобы исключить какое-либо влияние на генерацию тестовых данных, я написал все это в файл и теперь прочитал его оттуда.

 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

"Любое: 00: 00: 00.1640164 Есть: 00: 00: 00.0750075"

5M

"Любое: 00: 00: 00.0810081 Есть: 00: 00: 00.0360036"

"Любое: 00: 00: 00.0190019 Существует: 00: 00: 00.0070007"

500k

"Любой: 00: 00: 00.0120012 Существует: 00: 00: 00.0040004"

введите описание изображения здесь

Ответ 4

В качестве продолжения ответа Matas на бенчмаркинг.

TL/DR: Exists() и Any() одинаково быстры.

Во-первых: бенчмаркинг с использованием секундомера не является точным (см. ответ series0ne на другой, но знакомой теме), но это гораздо точнее, чем DateTime.

Чтобы получить действительно точные показания, используйте Performance Profiling. Но один из способов понять, как производительность двух методов зависит друг от друга, - это выполнить оба метода загружать раз, а затем сравнивать самое быстрое время выполнения каждого из них. Таким образом, не имеет значения, что JITing и другие шумы дают нам плохие показания (и это так), потому что оба исполнения "в некотором смысле ошибочны".

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

После выполнения вышеуказанного кода 4 раза (что в свою очередь делает 1 000 Exists() и Any() в списке с 1 000 000 элементов), нетрудно видеть, что методы в значительной степени одинаково быстры.

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

Есть небольшая разница, но это слишком маленькая разница, чтобы не объясняться фоновым шумом. Я предполагаю, что если бы вы сделали 10 000 или 100 000 Exists() и Any() вместо этого, то небольшое различие исчезло бы более или менее.

Ответ 5

Кроме того, это будет работать, только если значение имеет тип bool. Обычно это используется с предикатами. Любой предикат обычно используется для определения того, существует ли какой-либо элемент, удовлетворяющий заданному условию. Здесь вы просто делаете карту из своего элемента я в свойство bool. Он будет искать "i", значение Value которого истинно. После этого метод вернет true.

Ответ 6

Когда вы исправляете измерения - как упоминалось выше: Any и Exists и добавление среднего значения - мы получим следующий результат:

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.