如果我有两个列表并且我想知道是否至少有一个共同元素,我有两个选项:
lst1.Intersect(lst2).Any();
Lst1.Any(x => lst2.Contains(x));
Run Code Online (Sandbox Code Playgroud)
这两个选项给了我预期的结果,但是我不知道什么是最好的选择.哪个更有效率?为什么?
谢谢.
编辑:当我创建这篇文章时,除了解决方案之外,我正在寻找原因.我知道我可以运行测试,但我不知道结果的原因.一个比另一个快?总是一种解决方案最好吗?
所以出于这个原因,我接受了马修的答案,不仅是为了测试代码,而且还解释了什么时候比其他人好,为什么.我非常感谢尼古拉斯和奥伦的贡献.
谢谢.
Mat*_*son 15
奥伦的回答在秒表的使用方式上有误.Any()在测量所用的时间之后,它不会在循环结束时复位.
注意它是如何回到循环的开始,秒表永远不会Reset()使添加的时间intersect 包括所用的时间Any().
以下是更正后的版本.
在任何调试器外部运行的发布版本会在我的PC上显示此结果:
Intersect: 1ms
Any: 6743ms
Run Code Online (Sandbox Code Playgroud)
请注意我是如何为此测试制作两个不重叠的字符串列表.另请注意,这是最糟糕的测试.
如果有许多交叉点(或者在数据开始附近碰巧发生交叉点)那么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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
为了比较的目的,我编写了自己的测试比较int序列:
intersect took 00:00:00.0065928
any took 00:00:08.6706195
Run Code Online (Sandbox Code Playgroud)
代码:
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();
}
}
}
Run Code Online (Sandbox Code Playgroud)
如果我还通过将列表更改为此并将其增加到10000 来为重叠范围计时count:
var lst1 = Enumerable.Range(10000, 10000);
var lst2 = Enumerable.Range(10000, 10000);
Run Code Online (Sandbox Code Playgroud)
我得到这些结果:
intersect took 00:00:03.2607476
any took 00:00:00.0019170
Run Code Online (Sandbox Code Playgroud)
在这种情况下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().
当然,大部分时间都不重要!
除非您发现这部分代码成为瓶颈,否则这仅仅是学术兴趣.如果没有问题,你不应该浪费时间进行这种分析.:)