如何为二维LINQ操作创建高效的数据结构?

Gei*_*erg 5 .net c# linq performance

问题:我有2个类型的对象,让他们打电话BuildingImprovement.大约有30个Improvement实例,而可能有1-1000 个实例Building.对于的每个组合BuildingImprovement,我不得不执行一些繁重的计算,并且将结果存储在一个Result对象.

两个BuildingS和ImprovementS可通过一个整数ID来表示.

然后我需要能够:

  • 访问Result给定BuildingImprovement有效(编辑:请参阅下面的评论)
  • 对给定的Result所有Improvements 执行聚合Building,如.Sum()和.Average()
  • 对于给定的Result所有Buildings,对s 执行相同的聚合Improvement

这将发生在Web服务器后端,因此内存可能是一个问题,但速度是最重要的.

思念至今:

  1. 使用Dictionary<Tuple<int, int>, Result>with <BuildingID, ImprovementID>作为键.这应该给我快速插入和单个查找,但我关心.Where().Sum()性能.
  2. 使用二维数组,其中一个维度用于BuildingIDs,一个用于ImprovementIDs,以及Resultas值.另外,构建两个Dictionary<int, int>BuildingIDs和ImprovementIDs 映射到它们各自的数组行/列索引.这可能意味着最多1000+ Dictionarys,这会是一个问题吗?
  3. 用一个List<Tuple<int, int, Result>>.我认为这可能效率最低,有O(n)插入,但我可能是错的.

我在这里错过了一个明显更好的选择吗?

编辑:原来它只是我感兴趣的聚合值(每个Building和每个Improvement); 看到我的回答.

Gei*_*erg 0

感谢您的回答,测试代码非常有用:)

对我来说,解决方案是放弃 LINQ,并在繁重的计算之后直接手动执行聚合,因为无论如何我都必须迭代构建和改进的每个组合。

另外,我必须使用对象本身作为键,以便在将对象持久化到实体框架之前执行计算(即它们的 ID 均为 0)。

代码:

public class Building {
    public int ID { get; set; }
    ...
}

public class Improvement {
    public int ID { get; set; }
    ...
}

public class Result {
    public decimal Foo { get; set; }
    public long Bar { get; set; }
    ...

    public void Add(Result result) {
        Foo += result.Foo;
        Bar += result.Bar;
        ...
    }   
}

public class Calculator {
    public Dictionary<Building, Result> ResultsByBuilding;
    public Dictionary<Improvement, Result> ResultsByImprovement;

    public void CalculateAndAggregate(IEnumerable<Building> buildings, IEnumerable<Improvement> improvements) {
        ResultsByBuilding = new Dictionary<Building, Result>();
        ResultsByImprovement = new Dictionary<Improvement, Result>();
        for (building in buildings) {
            for (improvement in improvements) {
                Result result = DoHeavyCalculation(building, improvement);

                if (ResultsByBuilding.ContainsKey(building)) {
                    ResultsByBuilding[building].Add(result);
                } else {
                    ResultsByBuilding[building] = result;
                }

                if (ResultsByImprovement.ContainsKey(improvement)) {
                    ResultsByImprovement[improvement].Add(result);
                } else {
                    ResultsByImprovement[improvement] = result;
                }
            }
        }
    }
}

public static void Main() {
    var calculator = new Calculator();
    IList<Building> buildings = GetBuildingsFromRepository();
    IList<Improvement> improvements = GetImprovementsFromRepository();
    calculator.CalculateAndAggregate(buildings, improvements);
    DoStuffWithResults(calculator);
}
Run Code Online (Sandbox Code Playgroud)

我这样做是因为我确切地知道我想要哪些聚合;如果我需要一种更动态的方法,我可能会选择 @MatthewWatson 的字典之类的东西。