C# 列表迭代性能

C# 列表迭代性能,c#,.net,performance,C#,.net,Performance,我有一个for循环,它总共进行24次迭代,每次迭代代表一天中的一个小时,然后在另一个嵌套的for循环中每隔15分钟检查一次。另一个嵌套检查列表中的小时和分钟值,然后聚合列表中的某些项目(如果它们满足我的时间要求)。问题是我的列表最多可以包含100万条记录,这意味着我可以24*4次遍历100万条记录 在这种情况下,如何优化代码以提高性能?我知道这可能可以用LINQ语句简化,但我不确定它是否会更快。下面是我正在做的一个例子 List<SummaryData> Aggregates = n

我有一个for循环,它总共进行24次迭代,每次迭代代表一天中的一个小时,然后在另一个嵌套的for循环中每隔15分钟检查一次。另一个嵌套检查列表中的小时和分钟值,然后聚合列表中的某些项目(如果它们满足我的时间要求)。问题是我的列表最多可以包含100万条记录,这意味着我可以24*4次遍历100万条记录

在这种情况下,如何优化代码以提高性能?我知道这可能可以用LINQ语句简化,但我不确定它是否会更快。下面是我正在做的一个例子

List<SummaryData> Aggregates = new List<SummaryData>();
for(int startHour = 0; startHour < 24; startHour++)
{
   for(int startMin = 0; startMin < 60; startMin+= 15)
   {
      int aggregateData = 0;
      //My ItemList can have up to 1 million records.
      foreach(ListItem item in ItemList)
      {
         if((item.time.Hour == startHour)&&(item.time.Minute == startMinute))
         {
            aggregateData += item.number;
         }
      }
         SummaryData aggregate = new SummaryData { SummaryId = item.id, TotalNumber = aggregateData
         Aggregates.Add(aggregate);

   }
}
class SummaryData
{
   public int SummaryId {get; set;}
   public int TotalNumber {get; set;}
}

不要在每一项中查找每一小时和每一分钟,只需在ItemList上迭代一次,然后根据每个item.time.Hour和item.time.Minute采取行动。

不要在每一项中查找每一小时和每一分钟,只需在ItemList上迭代一次,并根据每个item.time.Hour和item.time.Minute采取行动。

我将大致如下组织数据:

另见:


我大概会这样组织数据:

另见:


根据上面的逻辑,您应该只需要迭代列表一次。您可以将for循环嵌套在foreach中,并可能获得更好的性能。我还将使用字典保存聚合数据,并将其键基于总分钟数,即小时*60+分钟


根据上面的逻辑,您应该只需要迭代列表一次。您可以将for循环嵌套在foreach中,并可能获得更好的性能。我还将使用字典保存聚合数据,并将其键基于总分钟数,即小时*60+分钟


这个算法的结果是什么?如果我没有得到它,我会道歉

它似乎标识itemList中分钟值可被15整除的所有项目,然后将其数值添加到运行计数器中,然后将运行计数器添加到该聚合对象中

因为我不清楚这些物体的类型,我对这里实际发生的事情有点模糊。您似乎使用aggregateData+=item.number聚合了一次,然后使用Aggregates.AddaggregateData再次聚合。您确定没有对这些内容进行双重求和吗?我甚至不清楚您是在尝试对限定项的值求和,还是创建它们的列表

除此之外,24*4次浏览100万个项目的整个列表肯定不是必需的,也不是最佳的,但如果没有对目标的更清晰理解,我无法确定什么是正确的


正如其他答案中所建议的,正确的方法可能会在itemList上迭代一次并对每个项目进行操作,而不是迭代100次并丢弃列表中的每个项目99次,因为您知道它只能满足100次迭代中的一次。

此算法的结果是什么?如果我没有得到它,我会道歉

它似乎标识itemList中分钟值可被15整除的所有项目,然后将其数值添加到运行计数器中,然后将运行计数器添加到该聚合对象中

因为我不清楚这些物体的类型,我对这里实际发生的事情有点模糊。您似乎使用aggregateData+=item.number聚合了一次,然后使用Aggregates.AddaggregateData再次聚合。您确定没有对这些内容进行双重求和吗?我甚至不清楚您是在尝试对限定项的值求和,还是创建它们的列表

除此之外,24*4次浏览100万个项目的整个列表肯定不是必需的,也不是最佳的,但如果没有对目标的更清晰理解,我无法确定什么是正确的


正如其他答案中所建议的,正确的方法可能是在itemList上迭代一次并对每个项目进行操作,而不是迭代100次并丢弃列表中的每个项目99次,因为您知道它只能满足100次迭代中的一次。

您的问题陈述有点模糊。看起来您需要一个按项目id列出的摘要,提供时间戳位于整数四分之一小时边界上的所有项目编号的总和

我认为下面的方法应该可以奏效

一次通过列表 数据存储是一个高度平衡的二叉树,因此查找、插入和删除都是Olog N。 代码如下:

public class SummaryData
{
  public SummaryData( int id )
  {
    this.SummaryId   = id ;
    this.TotalNumber = 0  ;
  }
  public int SummaryId   { get; set; }
  public int TotalNumber { get; set; }
}

public class ListItem
{
  public int      Id     ;
  public int      Number ;
  public DateTime Time   ;
}

public IEnumerable<SummaryData> Summarize( IEnumerable<ListItem> ItemList )
{
  const long                        TICKS_PER_QUARTER_HOUR = TimeSpan.TicksPerMinute * 15;
  SortedDictionary<int,SummaryData> summary                = new SortedDictionary<int , SummaryData>();

  foreach ( ListItem item in ItemList )
  {
    long TimeOfDayTicks     = item.Time.TimeOfDay.Ticks;
    bool on15MinuteBoundary = ( 0 == TimeOfDayTicks % TICKS_PER_QUARTER_HOUR ? true : false );

    if ( on15MinuteBoundary )
    {
      int         key      = (int)( TimeOfDayTicks / TICKS_PER_QUARTER_HOUR );
      SummaryData value;
      bool        hasValue = summary.TryGetValue( key , out value );

      if ( !hasValue )
      {
        value = new SummaryData( item.Id );
        summary.Add( value.SummaryId , value ) ;
      }
      value.TotalNumber += item.Number;

    }

  }

  return summary.Values;

}

你的问题陈述有点模糊。看起来您需要一个按项目id列出的摘要,提供时间戳位于整数四分之一小时边界上的所有项目编号的总和

我认为下面的方法应该可以奏效

一次通过列表 数据存储是一个高度平衡的二叉树,因此查找、插入和删除都是Olog N。 代码如下:

public class SummaryData
{
  public SummaryData( int id )
  {
    this.SummaryId   = id ;
    this.TotalNumber = 0  ;
  }
  public int SummaryId   { get; set; }
  public int TotalNumber { get; set; }
}

public class ListItem
{
  public int      Id     ;
  public int      Number ;
  public DateTime Time   ;
}

public IEnumerable<SummaryData> Summarize( IEnumerable<ListItem> ItemList )
{
  const long                        TICKS_PER_QUARTER_HOUR = TimeSpan.TicksPerMinute * 15;
  SortedDictionary<int,SummaryData> summary                = new SortedDictionary<int , SummaryData>();

  foreach ( ListItem item in ItemList )
  {
    long TimeOfDayTicks     = item.Time.TimeOfDay.Ticks;
    bool on15MinuteBoundary = ( 0 == TimeOfDayTicks % TICKS_PER_QUARTER_HOUR ? true : false );

    if ( on15MinuteBoundary )
    {
      int         key      = (int)( TimeOfDayTicks / TICKS_PER_QUARTER_HOUR );
      SummaryData value;
      bool        hasValue = summary.TryGetValue( key , out value );

      if ( !hasValue )
      {
        value = new SummaryData( item.Id );
        summary.Add( value.SummaryId , value ) ;
      }
      value.TotalNumber += item.Number;

    }

  }

  return summary.Values;

}

使用lamda表达式进行比较。只需使用Linq GroupBy和Sum扩展名:Consid
呃,改变你们的结构,比如改变一个排序列表或者散列图。我不能理解外循环的含义。它不是涵盖了小时内所有可能的值吗?我不完全确定您是否在尝试完成我有点累,因此如果不了解这一点,似乎应该可以将外部循环移动到内部循环中,例如,对每个列表项运行24*4测试,不是对整个列表进行24*4测试。使用lamda表达式进行比较。只需使用Linq GroupBy和Sum扩展:考虑更改您的结构,例如更改为排序列表或哈希映射。我无法理解外循环的含义。它不是涵盖了小时内所有可能的值吗?我不完全确定您是否在尝试完成我有点累,因此如果不了解这一点,似乎应该可以将外部循环移动到内部循环中,例如,对每个列表项运行24*4测试,不是对整个列表进行24*4测试。我认为更有效的方法是在分组后使用总和扩展。我确实认为像您建议的那样使用int键可能会更简单,但是在Linq中很容易更改approach@sehe:LINQ解决方案可能更具可读性,但由于OP寻求优化,这应该更快,LINQ将在分组时引入中间存储对象。我认为更有效的方法是在分组后使用总和扩展。我确实认为像您建议的那样使用int键可能会更简单,但是在Linq中很容易更改approach@sehe:LINQ解决方案可能更具可读性,但由于OP正在寻求优化,因此这应该更快。在分组时,LINQ将引入中间存储对象。Mmm。发现您希望将每15分钟分组后更新答案。。。抱歉读得太草率了。发现您希望将每15分钟分组后更新答案。。。抱歉读得太草率了
public class SummaryData
{
  public SummaryData( int id )
  {
    this.SummaryId   = id ;
    this.TotalNumber = 0  ;
  }
  public int SummaryId   { get; set; }
  public int TotalNumber { get; set; }
}

public class ListItem
{
  public int      Id     ;
  public int      Number ;
  public DateTime Time   ;
}

public IEnumerable<SummaryData> Summarize( IEnumerable<ListItem> ItemList )
{
  const long                        TICKS_PER_QUARTER_HOUR = TimeSpan.TicksPerMinute * 15;
  SortedDictionary<int,SummaryData> summary                = new SortedDictionary<int , SummaryData>();

  foreach ( ListItem item in ItemList )
  {
    long TimeOfDayTicks     = item.Time.TimeOfDay.Ticks;
    bool on15MinuteBoundary = ( 0 == TimeOfDayTicks % TICKS_PER_QUARTER_HOUR ? true : false );

    if ( on15MinuteBoundary )
    {
      int         key      = (int)( TimeOfDayTicks / TICKS_PER_QUARTER_HOUR );
      SummaryData value;
      bool        hasValue = summary.TryGetValue( key , out value );

      if ( !hasValue )
      {
        value = new SummaryData( item.Id );
        summary.Add( value.SummaryId , value ) ;
      }
      value.TotalNumber += item.Number;

    }

  }

  return summary.Values;

}