我想使用可能需要一些时间来计算的值来对对象列表进行排序。现在我有这样的代码:
public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
return foos.OrderByDescending(foo => CalculateBar(foo));
}
private int CalculateBar(Foo foo)
{
//some slow process here
}
上面代码的问题在于它会为每个项目调用多次计算值,这是不好的。可能的优化是使用缓存值(可能是字典),但这意味着SortFoo必须在每次排序后清除缓存(以避免内存泄漏,我确实希望在每个{{1}上重新计算值呼叫)。
这个问题是否有更清洁,更优雅的解决方案?
答案 0 :(得分:4)
由于每个项目在排序中多次与其他项目进行比较,因此您可以低成本地至少每个项目缓存一次计算。 如果您经常针对相同的值运行计算,则记住该函数将是您最好的选择,
public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
return foos
.Select(f => new { Foo = f, SortBy = CalculateBar(f) })
.OrderByDescending(f=> f.SortBy)
.Select(f => f.Foo);
}
这会将计算减少到每个项目一次
答案 1 :(得分:4)
.OrderBy()
似乎已针对慢keySelector
进行了优化。
根据以下内容,.OrderBy()
似乎会缓存您提供的keySelector
代表的结果。
var random = new Random(0);
var ordered = Enumerable
.Range(0, 10)
.OrderBy(x => {
var result = random.Next(20);
Console.WriteLine("keySelector({0}) => {1}", x, result);
return result;
});
Console.WriteLine(String.Join(", ", ordered));
这是输出:
keySelector(0) => 14
keySelector(1) => 16
keySelector(2) => 15
keySelector(3) => 11
keySelector(4) => 4
keySelector(5) => 11
keySelector(6) => 18
keySelector(7) => 8
keySelector(8) => 19
keySelector(9) => 5
4, 9, 7, 3, 5, 0, 2, 1, 6, 8
如果每次比较都运行一次委托,我发现每个项目只能调用一次keySelector
代表。