any VS .Exists -有什么区别?

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

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

而且

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

更新1

当我解汇编.Exists时,它看起来好像没有代码。

更新2

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

394320 次浏览

区别在于Any是System.Linq.Enumerable中定义的任何IEnumerable<T>的扩展方法。它可以用于任何IEnumerable<T>实例。

Exists似乎不是一种扩展方法。我猜coll的类型是List<T>。Exists是一个实例方法,它的功能非常类似于Any。

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

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

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

看文档

列表。存在(对象方法- MSDN)

确定List(T)是否包含与指定谓词定义的条件匹配的元素。

它从。net 2.0就存在了,所以在LINQ之前。应该与谓词委托一起使用,但lambda表达式向后兼容。还有,List有这个(甚至不是IList)

IEnumerable。任何(扩展方法- MSDN)

确定序列的任何元素是否满足条件。

这是. net 3.5中的新功能,并使用Func(TSource, bool)作为参数,因此这是用于lambda表达式和LINQ的。

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

TLDR; 在性能方面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;
}

1000万条记录

"任何: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”

有10万条记录

“存在:00:00:00.0010001任何:00:00:00.0020002”

Any似乎慢了2级。

对于5和10M记录,我改变了它生成列表的方式,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"))
{


}
}

好吧,为了消除产生测试数据的任何影响,我把它都写到了文件中,现在从那里读取。

 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米

"任何:00:00:00.1640164存在:00:00:00.0750075"

5米

"任何:00:00:00.0810081存在:00:00:00.0360036"

1米

"任何:00:00:00.0190019存在:00:00:00.0070007"

500 k

"任何:00:00:00.0120012存在:00:00:00.0040004"

enter image description here

作为马特斯的回答基准测试的延续。

TL /博士: Exists()和Any()同样快。

首先:使用Stopwatch进行基准测试并不精确(请看系列一关于另一个不同但相似的话题的回答),但它比DateTime精确得多。

获得真正精确读数的方法是使用性能分析。但是有一种方法可以了解这两个方法的性能是如何相互衡量的,那就是执行两个方法加载< em > < / em >的次数,然后比较每个方法的最快执行时间。这样,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次之后(依次在包含100万个元素的列表中执行1 000 Exists()Any()),不难看出这些方法几乎同样快。

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或10 000 Exists()Any()替换,这个微小的差异将或多或少地消失。

当你修正测量值时——如上所述: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.