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

C# 如何为二维LINQ操作创建有效的数据结构?,c#,.net,performance,linq,C#,.net,Performance,Linq,问题:我有两种对象,让我们称它们为建筑和改进。大约有30个改进实例,而可以有1-1000个构建s。对于建筑和改进的每个组合,我必须执行一些繁重的计算,并将结果存储在结果对象中 建筑s和改进s都可以用整数ID表示 然后,我需要能够: 高效访问给定建筑的结果和改进(编辑:请参阅下面的注释) 对于给定的建筑的所有改进s,对结果s执行聚合,如.Sum()和.Average() 对于给定的改进s,对所有建筑s的结果s执行相同的聚合 这将发生在web服务器后端,因此内存可能是一个问题,但速度是最重要的

问题:我有两种对象,让我们称它们为
建筑
改进
。大约有30个
改进
实例,而可以有1-1000个
构建
s。对于
建筑
改进
的每个组合,我必须执行一些繁重的计算,并将结果存储在
结果
对象中

建筑
s和
改进
s都可以用整数ID表示

然后,我需要能够:

  • 高效访问给定
    建筑的
    结果
    改进
    (编辑:请参阅下面的注释)
  • 对于给定的
    建筑
    的所有
    改进
    s,对
    结果
    s执行聚合,如.Sum()和.Average()
  • 对于给定的
    改进
    s,对所有
    建筑
    s的
    结果
    s执行相同的聚合
这将发生在web服务器后端,因此内存可能是一个问题,但速度是最重要的

迄今为止的想法:

  • 使用带有
    键的
    词典
    。这应该可以让我快速插入和单次查找,但我关心的是
    .Where()
    .Sum()
    性能
  • 使用二维数组,一个维度用于
    BuildingID
    s,一个维度用于
    ImprovementID
    s,而
    结果
    作为值。此外,构建两个
    Dictionary
    ,将
    BuildingID
    s和
    ImprovementID
    s映射到各自的数组行/列索引。这可能意味着最多1000+
    Dictionary
    s,这会有问题吗
  • 使用
    列表
    。我认为对于O(n)插入,这可能是效率最低的,尽管我可能错了
  • 我是否错过了一个明显更好的选择


    编辑:结果表明,我只对聚合值感兴趣(每
    建筑
    和每
    改进
    );看看我的答案。

    一般来说,字典是最有效的查找工具。当通过键访问时,查找效率和操作效率都是常数O(1)。这将有助于访问,第一点

    在第二种和第三种情况下,您需要遍历所有的项O(n),因此除了您希望按照指定的顺序O(n*n)遍历它们之外,没有其他方法可以加快进程-然后您可以使用SortedDictionray O(n),但会降低查找和操作效率(O(logn))

    因此,我将使用您发布的第一个解决方案。

    您可以使用“字典字典”来保存结果数据,例如:

    //             Building ID ↓               ↓ Improvement ID
    var data = new Dictionary<int, Dictionary<int, Result>>();
    
    使用字典字典的速度要快得多,但只有当您打算进行许多这样的聚合时,这才是重要的

    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Collections.Generic;
    
    namespace Demo
    {
        sealed class Result
        {
            public double Data;
        }
    
        sealed class BuildingId
        {
            public BuildingId(int id)
            {
                Id = id;
            }
    
            public readonly int Id;
    
            public override int GetHashCode()
            {
                return Id.GetHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var other = obj as BuildingId;
    
                if (other == null)
                    return false;
    
                return this.Id == other.Id;
            }
        }
    
        sealed class ImprovementId
        {
            public ImprovementId(int id)
            {
                Id = id;
            }
    
            public readonly int Id;
    
            public override int GetHashCode()
            {
                return Id.GetHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var other = obj as ImprovementId;
    
                if (other == null)
                    return false;
    
                return this.Id == other.Id;
            }
        }
    
        sealed class Building
        {
            public BuildingId Id;
            public int Value;
        }
    
        sealed class Improvement
        {
            public ImprovementId Id;
            public int Value;
        }
    
        sealed class BuildingResults : Dictionary<BuildingId, Result>{}
    
        sealed class ImprovementResults: Dictionary<ImprovementId, Result>{}
    
        sealed class BuildingsById: Dictionary<BuildingId, ImprovementResults>{}
    
        sealed class ImprovementsById: Dictionary<ImprovementId, BuildingResults>{}
    
        class Program
        {
            void run()
            {
                var byBuildingId    = CreateTestBuildingsById();            // Create some test data.
                var byImprovementId = CreateImprovementsById(byBuildingId); // Create the alternative lookup dictionaries.
                var testTuples      = CreateTestTuples();
    
                ImprovementId improvementId = new ImprovementId(5010);
    
                int count = 10000;
    
                Stopwatch sw = Stopwatch.StartNew();
    
                for (int i = 0; i < count; ++i)
                    byImprovementId[improvementId].Sum(result => result.Value.Data);
    
                Console.WriteLine("Dictionary of dictionaries took " + sw.Elapsed);
    
                sw.Restart();
    
                for (int i = 0; i < count; ++i)
                    testTuples.Where(result => result.Key.Item2.Equals(improvementId)).Sum(item => item.Value.Data);
    
                Console.WriteLine("Dictionary of tuples took " + sw.Elapsed);
            }
    
            static Dictionary<Tuple<BuildingId, ImprovementId>, Result> CreateTestTuples()
            {
                var result = new Dictionary<Tuple<BuildingId, ImprovementId>, Result>();
    
                for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
                    for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
                        result.Add(
                            new Tuple<BuildingId, ImprovementId>(new BuildingId(buildingKey), new ImprovementId(improvementKey)),
                            new Result
                            {
                                Data = buildingKey + improvementKey/1000.0
                            });
    
                return result;
            }
    
            static BuildingsById CreateTestBuildingsById()
            {
                var byBuildingId = new BuildingsById();
    
                for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
                {
                    var improvements = new ImprovementResults();
    
                    for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
                    {
                        improvements.Add
                        (
                            new ImprovementId(improvementKey),
                            new Result
                            {
                                Data = buildingKey + improvementKey/1000.0
                            }
                        );
                    }
    
                    byBuildingId.Add(new BuildingId(buildingKey), improvements);
                }
    
                return byBuildingId;
            }
    
            static ImprovementsById CreateImprovementsById(BuildingsById byBuildingId)
            {
                var byImprovementId = new ImprovementsById();
    
                foreach (var improvements in byBuildingId)
                {
                    foreach (var improvement in improvements.Value)
                    {
                        if (!byImprovementId.ContainsKey(improvement.Key))
                            byImprovementId[improvement.Key] = new BuildingResults();
    
                        byImprovementId[improvement.Key].Add(improvements.Key, improvement.Value);
                    }
                }
    
                return byImprovementId;
            }
    
            static void Main()
            {
                new Program().run();
            }
        }
    }
    
    使用系统;
    使用系统诊断;
    使用System.Linq;
    使用System.Collections.Generic;
    名称空间演示
    {
    密封类结果
    {
    公开双重数据;
    }
    密封类BuildingId
    {
    公共建筑id(内部id)
    {
    Id=Id;
    }
    公共只读int-Id;
    公共覆盖int GetHashCode()
    {
    返回Id.GetHashCode();
    }
    公共覆盖布尔等于(对象对象对象)
    {
    var other=obj作为BuildingId;
    如果(其他==null)
    返回false;
    返回this.Id==other.Id;
    }
    }
    密封级改进
    {
    公共改善(内部id)
    {
    Id=Id;
    }
    公共只读int-Id;
    公共覆盖int GetHashCode()
    {
    返回Id.GetHashCode();
    }
    公共覆盖布尔等于(对象对象对象)
    {
    var other=作为改进的obj;
    如果(其他==null)
    返回false;
    返回this.Id==other.Id;
    }
    }
    封闭式教学楼
    {
    公共建筑标识;
    公共价值观;
    }
    密封级改进
    {
    公共改善Id;
    公共价值观;
    }
    密封类构建结果:字典{}
    密封类改进结果:字典{}
    密封类BuildingById:Dictionary{}
    密封类改进BYID:字典{}
    班级计划
    {
    无效运行()
    {
    var byBuildingId=CreateTestBuildingById();//创建一些测试数据。
    var byImprovementId=CreateImprovementsById(byBuildingId);//创建可选的查找字典。
    var testTuples=CreateTestTuples();
    改良改良=新改良(5010);
    整数计数=10000;
    秒表sw=Stopwatch.StartNew();
    对于(int i=0;iresult.Value.Data);
    Console.WriteLine(“字典字典已占用”+sw.passed);
    sw.Restart();
    对于(int i=0;iresult.Key.Item2.Equals(improvementId)).Sum(item=>item.Value.Data);
    Console.WriteLine(“元组字典”+sw.appeased);
    }
    静态字典CreateTestTuples()
    {
    var result=newdictionary();
    对于(int buildingKey=1000;buildingKey<2000;++buildingKey)
    对于(int-improvementKey=5000;improvementKey<5030;++improvementKey)
    结果。添加(
    新元组(新BuildingId(buildingKey)、新ImprovementId(improvementKey)),
    新结果
    {
    数据=构建键+改进键/1000.0
    });
    返回结果;
    }
    静态BuildingById CreateTestBuildingById()
    {
    var by
    
    //                      Improvment ID ↓               ↓ Building ID
    var byImprovementId = new Dictionary<int, Dictionary<int, Result>>();
    
    using System;
    using System.Linq;
    using System.Collections.Generic;
    
    namespace Demo
    {
        sealed class Result
        {
            public double Data;
        }
    
        sealed class BuildingId
        {
            public BuildingId(int id)
            {
                Id = id;
            }
    
            public readonly int Id;
    
            public override int GetHashCode()
            {
                return Id.GetHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var other = obj as BuildingId;
    
                if (other == null)
                    return false;
    
                return this.Id == other.Id;
            }
        }
    
        sealed class ImprovementId
        {
            public ImprovementId(int id)
            {
                Id = id;
            }
    
            public readonly int Id;
    
            public override int GetHashCode()
            {
                return Id.GetHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var other = obj as ImprovementId;
    
                if (other == null)
                    return false;
    
                return this.Id == other.Id;
            }
        }
    
        sealed class Building
        {
            public BuildingId Id;
            public int Value;
        }
    
        sealed class Improvement
        {
            public ImprovementId Id;
            public int Value;
        }
    
        sealed class BuildingResults : Dictionary<BuildingId, Result>{}
    
        sealed class ImprovementResults: Dictionary<ImprovementId, Result>{}
    
        sealed class BuildingsById: Dictionary<BuildingId, ImprovementResults>{}
    
        sealed class ImprovementsById: Dictionary<ImprovementId, BuildingResults>{}
    
        class Program
        {
            void run()
            {
                var byBuildingId    = CreateTestBuildingsById();            // Create some test data.
                var byImprovementId = CreateImprovementsById(byBuildingId); // Create the alternative lookup dictionaries.
    
                // Aggregate data for all improvements for building with ID == 1500:
    
                BuildingId buildingId = new BuildingId(1500);
    
                var sum = byBuildingId[buildingId].Sum(result => result.Value.Data);
                Console.WriteLine(sum);
    
                // Aggregate data for all buildings with a given improvement.
    
                ImprovementId improvementId = new ImprovementId(5010);
    
                sum = byBuildingId.Sum(improvements =>
                {
                    Result result;
                    return improvements.Value.TryGetValue(improvementId, out result) ? result.Data : 0.0;
                });
    
                Console.WriteLine(sum);
    
                // Aggregate data for all buildings with a given improvement using byImprovementId.
                // This will be much faster than the above Linq.
    
                sum = byImprovementId[improvementId].Sum(result => result.Value.Data);
                Console.WriteLine(sum);
            }
    
            static BuildingsById CreateTestBuildingsById()
            {
                var byBuildingId = new BuildingsById();
    
                for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
                {
                    var improvements = new ImprovementResults();
    
                    for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
                    {
                        improvements.Add
                        (
                            new ImprovementId(improvementKey),
                            new Result
                            {
                                Data = buildingKey + improvementKey/1000.0
                            }
                        );
                    }
    
                    byBuildingId.Add(new BuildingId(buildingKey), improvements);
                }
    
                return byBuildingId;
            }
    
            static ImprovementsById CreateImprovementsById(BuildingsById byBuildingId)
            {
                var byImprovementId = new ImprovementsById();
    
                foreach (var improvements in byBuildingId)
                {
                    foreach (var improvement in improvements.Value)
                    {
                        if (!byImprovementId.ContainsKey(improvement.Key))
                            byImprovementId[improvement.Key] = new BuildingResults();
    
                        byImprovementId[improvement.Key].Add(improvements.Key, improvement.Value);
                    }
                }
    
                return byImprovementId;
            }
    
            static void Main()
            {
                new Program().run();
            }
        }
    }
    
    Dictionary of dictionaries took 00:00:00.2967741
    Dictionary of tuples took 00:00:07.8164672
    
    using System;
    using System.Diagnostics;
    using System.Linq;
    using System.Collections.Generic;
    
    namespace Demo
    {
        sealed class Result
        {
            public double Data;
        }
    
        sealed class BuildingId
        {
            public BuildingId(int id)
            {
                Id = id;
            }
    
            public readonly int Id;
    
            public override int GetHashCode()
            {
                return Id.GetHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var other = obj as BuildingId;
    
                if (other == null)
                    return false;
    
                return this.Id == other.Id;
            }
        }
    
        sealed class ImprovementId
        {
            public ImprovementId(int id)
            {
                Id = id;
            }
    
            public readonly int Id;
    
            public override int GetHashCode()
            {
                return Id.GetHashCode();
            }
    
            public override bool Equals(object obj)
            {
                var other = obj as ImprovementId;
    
                if (other == null)
                    return false;
    
                return this.Id == other.Id;
            }
        }
    
        sealed class Building
        {
            public BuildingId Id;
            public int Value;
        }
    
        sealed class Improvement
        {
            public ImprovementId Id;
            public int Value;
        }
    
        sealed class BuildingResults : Dictionary<BuildingId, Result>{}
    
        sealed class ImprovementResults: Dictionary<ImprovementId, Result>{}
    
        sealed class BuildingsById: Dictionary<BuildingId, ImprovementResults>{}
    
        sealed class ImprovementsById: Dictionary<ImprovementId, BuildingResults>{}
    
        class Program
        {
            void run()
            {
                var byBuildingId    = CreateTestBuildingsById();            // Create some test data.
                var byImprovementId = CreateImprovementsById(byBuildingId); // Create the alternative lookup dictionaries.
                var testTuples      = CreateTestTuples();
    
                ImprovementId improvementId = new ImprovementId(5010);
    
                int count = 10000;
    
                Stopwatch sw = Stopwatch.StartNew();
    
                for (int i = 0; i < count; ++i)
                    byImprovementId[improvementId].Sum(result => result.Value.Data);
    
                Console.WriteLine("Dictionary of dictionaries took " + sw.Elapsed);
    
                sw.Restart();
    
                for (int i = 0; i < count; ++i)
                    testTuples.Where(result => result.Key.Item2.Equals(improvementId)).Sum(item => item.Value.Data);
    
                Console.WriteLine("Dictionary of tuples took " + sw.Elapsed);
            }
    
            static Dictionary<Tuple<BuildingId, ImprovementId>, Result> CreateTestTuples()
            {
                var result = new Dictionary<Tuple<BuildingId, ImprovementId>, Result>();
    
                for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
                    for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
                        result.Add(
                            new Tuple<BuildingId, ImprovementId>(new BuildingId(buildingKey), new ImprovementId(improvementKey)),
                            new Result
                            {
                                Data = buildingKey + improvementKey/1000.0
                            });
    
                return result;
            }
    
            static BuildingsById CreateTestBuildingsById()
            {
                var byBuildingId = new BuildingsById();
    
                for (int buildingKey = 1000; buildingKey < 2000; ++buildingKey)
                {
                    var improvements = new ImprovementResults();
    
                    for (int improvementKey = 5000; improvementKey < 5030; ++improvementKey)
                    {
                        improvements.Add
                        (
                            new ImprovementId(improvementKey),
                            new Result
                            {
                                Data = buildingKey + improvementKey/1000.0
                            }
                        );
                    }
    
                    byBuildingId.Add(new BuildingId(buildingKey), improvements);
                }
    
                return byBuildingId;
            }
    
            static ImprovementsById CreateImprovementsById(BuildingsById byBuildingId)
            {
                var byImprovementId = new ImprovementsById();
    
                foreach (var improvements in byBuildingId)
                {
                    foreach (var improvement in improvements.Value)
                    {
                        if (!byImprovementId.ContainsKey(improvement.Key))
                            byImprovementId[improvement.Key] = new BuildingResults();
    
                        byImprovementId[improvement.Key].Add(improvements.Key, improvement.Value);
                    }
                }
    
                return byImprovementId;
            }
    
            static void Main()
            {
                new Program().run();
            }
        }
    }