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();
}
}
}