我有几个列表,我需要迭代才能执行计算。总之,List1是道路起点和终点(ID)的列表,List2是这些终点的各个速度样本的列表(每组终点有多个速度样本)。 List1的定义如下:
class RoadwaySegment
{
public int StartId {get; set;}
public int EndId {get; set;}
}
List2的定义如下:
class IndividualSpeeds
{
public int StartHour {get; set;}
public int StartMin {get; set;} //either 0,15,30,or 45
public int Speed {get; set;}
public int StartId {get; set;}
public int EndId {get; set;}
}
List3是我计算的结果,将包含每天15分钟的List1中道路段的平均速度。 List3看起来像这样:
class SummaryData
{
public string SummaryHour {get; set;}
public string SummaryMin {get; set;}
public int StartId {get; set;}
public int EndId {get; set;}
public int AvgSpeed {get; set;}
}
目前,为了生成List3,我遍历List1,然后遍历每天的24小时,然后每隔15分钟一小时。对于这些迭代中的每一个,我检查List2中的单个速度样本是否应该包含在我的道路段的平均速度计算中。所以,它看起来像这样:
var summaryList = new List<SummaryData>();
foreach (var segment in RoadwaySegments)
{
for(int startHour = 0; startHour < 24; startHour++)
{
for(int startMin = 0; startMin < 60; startMin+= 15)
{
int totalSpeeds = 0;
int numSamples = 0;
int avgSpeed = 0;
foreach(var speedSample in IndividualSpeeds)
{
if((segment.StartId == speedSample.StartId)&&(segment.EndId == speedSample.EndId)&&(speedSample.StartHour == startHour)&&(speedSample.StartMin == startMin))
{
if(speedSample.Speed > 0)
{
totalSpeeds += speedSample.Speed;
numSamples += 1;
}
}
}
SummaryData summaryItem = new SummaryData {SummaryHour = startHour, SummaryMin = startMin, StartId = segment.StartId, EndId = segment.EndId, AvgSpeed = totalSpeeds/numSamples;
summaryList.Add(summaryItem);
}
}
}
此代码的问题是List1可能有一百个道路段,但List2可能包含一百万或更多的速度样本记录,因此列表的子迭代非常耗时。有没有办法使用GroupBy / LINQ来提高此代码的性能和可读性?注意包含平均速度的条件 - 它必须大于0。
答案 0 :(得分:3)
这是未经测试的,但我认为它会起作用:
from segment in RoadwaySegments
join sample in IndividualSpeeds on new { segment.StartId, segment.EndId } equals new { sample.StartId, sample.EndId } into segmentSamples
from startHour in Enumerable.Range(0, 24)
from startMin in new[] { 0, 15, 30, 45 }
let startMinSamples = segmentSamples
.Where(sample => sample.Speed > 0)
.Where(sample => sample.StartHour == startHour)
.Where(sample => sample.StartMin == startMin)
.Select(sample => sample.Speed)
.ToList()
select new SummaryItem
{
StartId = segment.StartId,
EndId = segment.EndId,
SummaryHour = startHour,
SummaryMin = minute,
AvgSpeed = startMinSamples.Count <= 2 ? 0 : startMinSamples.Average()
};
主要思想是迭代段和样本列表一次,为每个段生成一组样本。然后,对于每个组,您将为每个组合生成小时和分钟以及摘要项。最后,计算小时/分钟组合中所有非零样本的平均速度。
这不太理想,因为您仍然会将段的样本迭代24 * 4次,但它比迭代整个样本列表要好得多。这应该让你走上正确的道路,希望你能进一步优化最后一点。
答案 1 :(得分:2)
如果您使用的是.net 4,我建议将其与Parallel Linq并行化。这在RoadwaySegments上是令人尴尬的平行。
其次,我不建议对子列表进行嵌套迭代,而是建议迭代该列表一次并创建一个List<IndividualSpeeds>
字典,其中包含Composite Key的StartId,EndId,StartHour和EndHour。在这个词典中进行查找将多更快,而不是重复迭代每个RoadwaySegment。
答案 2 :(得分:0)
以下是克里斯和布莱恩回答的补充说明:
既然你说可能有数百万个速度样本记录,你只是不希望迭代它们的次数超过最小值 - 即你应该对它们进行分组(使用GroupBy
或Join
运算符),根据他们的开始时间和分钟。比你可以迭代每个组,并将每个样本记录添加到RoadwaySegments的某些字典(类似Dictionary<RoadwaySegment, IEnumerable<IndividalSpeeds>>
)并从该字典创建摘要项目。