好吧,FindAll 将匹配的元素复制到一个新列表中,而 Where 只是返回一个惰性求值的序列 - 不需要复制。
因此,我希望 Where 比 FindAll 稍微快一点,即使结果序列已被完全评估 - 当然Where 的惰性评估策略意味着如果你只看(比如说)第一场比赛,它不需要检查列表的其余部分。 (正如 Matthew 所指出的,维护Where 的状态机是有工作的。但是,这只会有固定的内存成本——而构建一个新列表可能需要多个数组分配等)
基本上,FindAll(predicate) 更接近于Where(predicate).ToList() 而不仅仅是Where(predicate)。
只是为了对 Matthew 的回答做出更多反应,我认为他的测试还不够彻底。他的谓词恰好选择了一半项。这是一个简短但完整的程序,它测试同一个列表但使用三个不同的谓词 - 一个不选择任何项目,一个选择所有项目,一个选择一半。在每种情况下,我都会运行 50 次测试以获得更长的时间。
我使用Count() 来确保对Where 结果进行全面评估。结果显示,收集了大约一半的结果,两者并驾齐驱。未收集任何结果,FindAll 获胜。收集所有结果,Where 获胜。我发现这很有趣:随着找到越来越多的匹配项,所有解决方案都会变得更慢:FindAll 需要进行更多复制,Where 必须返回匹配的值,而不仅仅是在 MoveNext() 实现中循环。但是,FindAll 的速度比Where 慢,因此失去了早期的领先优势。很有趣。
结果:
FindAll: All: 11994
Where: All: 8176
FindAll: Half: 6887
Where: Half: 6844
FindAll: None: 3253
Where: None: 4891
(使用 /o+ /debug- 编译并从命令行运行,.NET 3.5。)
代码:
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
class Test
{
static List<int> ints = Enumerable.Range(0, 10000000).ToList();
static void Main(string[] args)
{
Benchmark("All", i => i >= 0); // Match all
Benchmark("Half", i => i % 2 == 0); // Match half
Benchmark("None", i => i < 0); // Match none
}
static void Benchmark(string name, Predicate<int> predicate)
{
// We could just use new Func<int, bool>(predicate) but that
// would create one delegate wrapping another.
Func<int, bool> func = (Func<int, bool>)
Delegate.CreateDelegate(typeof(Func<int, bool>), predicate.Target,
predicate.Method);
Benchmark("FindAll: " + name, () => ints.FindAll(predicate));
Benchmark("Where: " + name, () => ints.Where(func).Count());
}
static void Benchmark(string name, Action action)
{
GC.Collect();
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < 50; i++)
{
action();
}
sw.Stop();
Console.WriteLine("{0}: {1}", name, sw.ElapsedMilliseconds);
}
}