关于c#:Linq .Any VS.Exists – 有什么区别?

Linq .Any VS .Exists - What's the difference?

在集合上使用LINQ,以下代码行之间的区别是什么?

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

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

更新1

当我分解.Exists时,似乎没有代码。

更新2

有人知道为什么没有这个代码吗?


参见文档

list.exists(对象方法-msdn)

Determines whether the List(T) contains elements that match the conditions defined by the specified predicate.

这在.NET 2.0之后就存在了,所以在Linq之前。用于谓词委托,但lambda表达式向后兼容。另外,just list还有这个(甚至不是ilist)

IEnumerable.Any(扩展方法-msdn)

Determines whether any element of a sequence satisfies a condition.

这是.NET3.5中的一个新特性,使用func(tsource,bool)作为参数,因此它被用于lambda表达式和linq。

在行为上,它们是相同的。


区别在于any是在system.linq.Enumerable上定义的任何IEnumerable的扩展方法。它可以用于任何IEnumerable实例。

exists似乎不是扩展方法。我猜Coll是List型的。如果存在,则是一个实例方法,其功能与任何实例方法都非常相似。

简而言之,这些方法基本上是相同的。一个比另一个更普遍。

  • any也有一个不带参数的重载,它只查找可枚举的中的任何项。
  • 存在没有这样的过载。


tldr;性能方面的Any似乎较慢(如果我正确设置了此设置,以便几乎同时评估这两个值)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
        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"))
            {

            }

测试列表生成器:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
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万条记录

" Any: 00:00:00.3770377 Exists: 00:00:00.2490249"

五百万记录

" Any: 00:00:00.0940094 Exists: 00:00:00.1420142"

1M记录

" Any: 00:00:00.0180018 Exists: 00:00:00.0090009"

对于500k,(我还翻转了它们被评估的顺序,看看是否没有与任何第一次运行相关联的附加操作。)

" Exists: 00:00:00.0050005 Any: 00:00:00.0100010"

有10万条记录

" Exists: 00:00:00.0010001 Any: 00:00:00.0020002"

似乎Any的速度慢了2级。

编辑:对于5和10万条记录,我改变了它生成列表的方式,Exists突然变得比Any慢,这意味着我测试的方式有问题。

新的测试机制:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
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:好的,为了消除生成测试数据的影响,我把它全部写到文件中,然后从文件中读取。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
 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米

" Any: 00:00:00.1640164 Exists: 00:00:00.0750075"

5m

" Any: 00:00:00.0810081 Exists: 00:00:00.0360036"

1m

" Any: 00:00:00.0190019 Exists: 00:00:00.0070007"

500 K

" Any: 00:00:00.0120012 Exists: 00:00:00.0040004"

enter image description here


作为Matas对基准测试的回答的延续。

tl/dr:exists()和any()速度相同。

首先:使用秒表进行基准测试是不精确的(请参阅系列0ne的另一个类似主题的答案),但它远比日期时间更精确。

获得真正精确读数的方法是使用性能分析。但是,了解这两个方法的性能如何相互衡量的一种方法是执行两个方法的时间负载,然后比较每个方法的最快执行时间。这样一来,抖动和其他噪音给我们带来糟糕的读数并不重要(它的确如此),因为在某种意义上,两次执行都是"同样误导"。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
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
Fastest 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次之后(依次执行1000个Exists()Any(),在一个包含1000 000个元素的列表中),不难看出这些方法的速度相当快。

1
2
3
4
5
6
7
8
9
10
11
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

有一点差别,但差别太小,不能用背景噪音来解释。我的猜测是,如果一个人做100000或100000个Exists()Any(),那么细微的差别会或多或少消失。


当您纠正测量值(如上所述):Any和Exists,并添加Average时,我们将得到以下输出:

1
2
3
4
5
6
7
8
9
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.

此外,只有当值为bool类型时,这才有效。通常这与谓词一起使用。通常使用任何谓词来确定是否有满足给定条件的元素。在这里,你只需要做一个从元素i到bool属性的映射。它将搜索值属性为真的"i"。一旦完成,该方法将返回true。