为什么我们必须在lambda表达式中的任何本机容器上使用Contains()
来避免成员资格查找?
答案 0 :(得分:2)
让我们考虑以下几点:
var source = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var matches = new[] { 12, 2, 19, 8, 40, 12, 123, 121, 1212, 122334, 2, 102, 10, 29 };
foreach (var item in source.Where(i => matches.Contains(i)))
{
Console.WriteLine(item);
}
现在,让我们考虑一下matches
i
中需要比较的项目数,然后再知道是否要写出来:
1: 14
2: 2
3: 14
5: 14
6: 14
7: 14
8: 4
9: 14
10: 13
虽然我们有时会很快得到答案,但我们通常不会特别提到答案是我们不应该这样做,所以我们进行了103次比较来处理10个项目。我们还浪费时间比较我们已经检查过的值(2
在matches
两次的事实)。
我们正在执行O(m)
次操作n次,因此算法为O(nm)
。
我们可以做得更好:
var source = new[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
var matches = new HashSet<int> { 12, 2, 19, 8, 40, 12, 123, 121, 1212, 122334, 2, 102, 10, 29 };
foreach (var item in source.Where(i => matches.Contains(i)))
{
Console.WriteLine(item);
}
由于哈希集上的Contains
为O(1)
,因此算法总体上为O(n)
,这与它的效果差不多。
要注意的一些重要事项是:
如果以上是在数据库上完成的,那么无论类型matches
是什么,它都会变成SQL生成的CONTAINS
子句的WHERE
部分,所以它无关紧要(CONTAINS
仍然比=
慢到一定数量的链接=
,但游戏中的因素不同。
使用相对较小的匹配项,O(n)
成本Contains
比创建和搜索哈希集的常量成本更便宜。 (以上示例可能需要大约相同的时间才能运行)。
所以这不是永远不会做的事情,而是你应该了解成本以及它们如何受源数据及其运行方式的影响,以及如何在成本特别高的情况下提高性能。< / p>
我们使用lambdas并不是特别重要,除了lambdas让我们快速编写一些代码让我们快速编写一些隐藏低效的代码,而如果我们必须写两个单独的foreach
循环算法O(nm)
会更明显。