在集合上使用Linq,以下代码行之间有什么区别?
if(!coll.Any(i => i.Value))
和
if(!coll.Exists(i => i.Value))
更新1
当我反汇编.Exists
看起来没有代码。
更新2
任何人都知道为什么这个没有代码?
#1楼
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;
}
拥有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”
拥有100k记录
“存在: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"))
{
}
}
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”
1M
“任何:00:00:00.0190019存在:00:00:00.0070007”
500K
“任何:00:00:00.0120012存在:00:00:00.0040004”
#2楼
TL / DR :Exists()和Any()同样快。
首先:使用秒表进行基准测试并不精确( 请参阅series0ne关于不同但类似的主题的答案 ),但它比DateTime精确得多。
获得真正精确读数的方法是使用性能分析。 但是,了解两种方法的性能如何相互衡量的一种方法是通过执行两种方法加载次数,然后比较每种方法的最快执行时间。 这样,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 000个元素的列表上执行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万Exists()
和Any()
,那么这个微小的差异会或多或少地消失。
#3楼
当您更正测量值时 - 如上所述:任意和存在,并添加平均值 - 我们将获得以下输出:
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.
#4楼
区别在于Any是System.Linq.Enumerable上定义的任何IEnumerable<T>
的扩展方法。 它可以在任何IEnumerable<T>
实例上使用。
存在似乎不是一种扩展方法。 我的猜测是coll的类型为List<T>
。 如果是这样Exists是一个实例方法,其功能与Any非常相似。
简而言之 , 这些方法基本相同。 一个比另一个更普遍。
- 任何也有一个不带参数的重载,只是查找枚举中的任何项目。
- 存在没有这种过载。
#5楼
此外,这仅在Value为bool类型时才有效。 通常,这与谓词一起使用。 通常使用任何谓词来查找是否存在满足给定条件的任何元素。 在这里,您只需要从元素i到bool属性进行映射。 它将搜索Value属性为true的“i”。 完成后,该方法将返回true。
来源:oschina
链接:https://my.oschina.net/u/3797416/blog/3164929