2

我想使用可能需要一些时间来计算的值对对象列表进行排序。现在我有这样的代码:

public IEnumerable<Foo> SortFoo(IEnumerable<Foo> original)
{
    return foos.OrderByDescending(foo => CalculateBar(foo));
}

private int CalculateBar(Foo foo)
{
    //some slow process here
}

上面代码的问题是它会为每个项目调用多次计算值,这不好。可能的优化是使用缓存值(可能是字典),但这意味着 SortFoo 必须在每次排序后清除缓存(以避免内存泄漏,我确实希望在每次SortFoo调用时重新计算该值)。

有没有更干净、更优雅的解决方案来解决这个问题?

4

2 回答 2

6

似乎.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委托。

于 2014-06-18T16:53:34.543 回答
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);
}

这会将计算减少到每个项目一次

于 2012-07-11T10:56:17.220 回答