Oren 的回答在使用秒表的方式上存在错误。在测量所用时间后,它不会在循环结束时重置Any()
。
请注意它如何回到循环的开始,而秒表从不存在,Reset()
因此添加到的时间intersect
包括Any()
.
以下是修正版。
在任何调试器之外运行的发布版本会在我的 PC 上给出以下结果:
Intersect: 1ms
Any: 6743ms
请注意我如何为此测试制作两个不重叠的字符串列表。另请注意,这是一个最坏情况测试。
如果有许多交叉点(或恰好在数据开始附近发生的交叉点),那么 Oren 说Any()
应该更快是完全正确的。
如果真实数据通常包含交叉点,那么最好使用Any()
. 否则,使用Intersect()
. 它非常依赖数据。
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Demo
{
class Program
{
void run()
{
double intersect = 0;
double any = 0;
Stopwatch stopWatch = new Stopwatch();
List<string> L1 = Enumerable.Range(0, 10000).Select(x => x.ToString()).ToList();
List<string> L2 = Enumerable.Range(10000, 10000).Select(x => x.ToString()).ToList();
for (int i = 0; i < 10; i++)
{
stopWatch.Restart();
Intersect(L1, L2);
stopWatch.Stop();
intersect += stopWatch.ElapsedMilliseconds;
stopWatch.Restart();
Any(L1, L2);
stopWatch.Stop();
any += stopWatch.ElapsedMilliseconds;
}
Console.WriteLine("Intersect: " + intersect + "ms");
Console.WriteLine("Any: " + any + "ms");
}
private static bool Any(List<string> lst1, List<string> lst2)
{
return lst1.Any(lst2.Contains);
}
private static bool Intersect(List<string> lst1, List<string> lst2)
{
return lst1.Intersect(lst2).Any();
}
static void Main()
{
new Program().run();
}
}
}
出于比较目的,我编写了自己的测试比较int
序列:
intersect took 00:00:00.0065928
any took 00:00:08.6706195
编码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
namespace Demo
{
class Program
{
void run()
{
var lst1 = Enumerable.Range(0, 10000);
var lst2 = Enumerable.Range(10000, 10000);
int count = 10;
DemoUtil.Time(() => lst1.Intersect(lst2).Any(), "intersect", count);
DemoUtil.Time(() => lst1.Any(lst2.Contains), "any", count);
}
static void Main()
{
new Program().run();
}
}
static class DemoUtil
{
public static void Print(this object self)
{
Console.WriteLine(self);
}
public static void Print(this string self)
{
Console.WriteLine(self);
}
public static void Print<T>(this IEnumerable<T> self)
{
foreach (var item in self)
Console.WriteLine(item);
}
public static void Time(Action action, string title, int count)
{
var sw = Stopwatch.StartNew();
for (int i = 0; i < count; ++i)
action();
(title + " took " + sw.Elapsed).Print();
}
}
}
如果我还通过将列表更改为此并将其增加到 10000来为重叠范围计时:count
var lst1 = Enumerable.Range(10000, 10000);
var lst2 = Enumerable.Range(10000, 10000);
我得到这些结果:
intersect took 00:00:03.2607476
any took 00:00:00.0019170
在这种情况下Any()
显然要快得多。
结论
最坏情况下的性能对 来说非常糟糕Any()
但可以接受Intersect()
。最佳情况下的性能Any()
对Intersect()
. (最好的情况Any()
可能是最坏的情况Intersect()
!)
该Any()
方法在最坏情况下为 O(N^2),在最佳情况下为 O(1)。该Intersect()
方法总是 O(N) (因为它使用散列,而不是排序,否则它将是 O(N(Log(N)))。
您还必须考虑内存使用情况:该Intersect()
方法需要获取其中一个输入的副本,Any()
而不需要。
因此,要做出最佳决策,您确实需要了解真实数据的特征,并实际执行测试。
如果您真的不希望Any()
在最坏的情况下变成 O(N^2),那么您应该使用Intersect()
. 但是,您最好使用Any()
.
当然,大多数时候这些都不重要!
除非您发现这部分代码是一个瓶颈,否则这只是学术兴趣。如果没有问题,您不应该在这种分析上浪费时间。:)