C#列表迭代性能

时间:2011-09-15 16:04:14

标签: c# .net performance

我有一个for循环,它执行24次迭代,每次迭代代表一天中的一个小时,然后在另一个嵌套for循环中检查每个15分钟的间隔。另一个嵌套检查List的小时和分钟值,然后在满足我的时间要求时聚合我的List中的一些项目。问题是我的列表可以包含多达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;}
}

5 个答案:

答案 0 :(得分:4)

不是在每个Hour中查找每个Minuteitem,而是只对ItemList进行一次迭代,并根据每个item.time.Hour和{{进行操作1}}。

答案 1 :(得分:4)

鉴于您的逻辑,您应该只需要迭代列表一次。您可以将for循环嵌套在foreach中,并可能获得更好的效果。我还会使用Dictionary来保存您的汇总数据,并将其密钥基于总分钟数(意为hour * 60 + minute)。

Dictionary<int, AggregateDate> aggregate = new Dictionary<int, AggregateData>();

foreach(ListItem item in ItemList)
{
    int key = item.Hour * 60 + item.Minute;

    AggregateData data;

    if(!aggregate.TryGetValue(key, out data))
    {
        aggregate.Add(key, data = new AggregateData());
    }

    data.Number += item.Number;
}

答案 2 :(得分:1)

我将大致像这样组织数据:

(另见:http://ideone.com/dyfoD

using System;
using System.Linq;
using System.Collections.Generic;

public class P
{
    struct DataItem
    {
        public System.DateTime time;
        public int number;
    }

    public static void Main(string[] args)
    {
        var ItemList = new DataItem[] {} ;
        var groups = ItemList
            .GroupBy(item => item.time.Hour * 60 + (item.time.Minute/15)*15 );
        var sums   = groups
            .ToDictionary(g => g.Key, g => g.Sum(item => item.number));


        // lookups now become trivially easy:

        int slot1900 = sums[1900];
        int slot1915 = sums[1915];
        int slot1930 = sums[1930];
    }
}

答案 3 :(得分:0)

此算法的结果是什么?抱歉,如果我因为没有得到它而感到愚蠢。

它似乎识别itemList中的所有项目,其分钟值可以被15整除,然后将其数值添加到正在运行的计数器中,然后将该运行计数器添加到此Aggregates对象中。

因为我不清楚其中某些物体的类型,所以我对这里发生的事情有点模糊。您似乎使用“aggregateData + = item.number”聚合一次,然后使用“Aggregates.Add(aggregateData)”聚合AGAIN,您确定不会对这些内容进行双重求和吗?如果您正在尝试对合格项目的值进行求和或创建它们列表,我甚至都不清楚。

除此之外,绝对没有必要或最优的方式24 * 4次遍历100万件物品的整个清单,但如果没有更清楚地了解目标,我无法确定什么是正确的。

正如其他答案中所建议的那样,正确的方法很可能只对itemList进行一次迭代并对每一项进行操作,而不是迭代~100次并丢弃列表中的每个项目~99次(因为你知道它可以只有资格参加~100次迭代之一。)

答案 4 :(得分:0)

你的问题陈述有点模糊。看起来您想要一个按项目ID的摘要,为您提供所有项目编号的总和,其中时间戳位于整数的四分之一小时边界内。

我认为以下应该可以解决问题。

  • 一次通过列表
  • 数据存储区是SortedDictionary(高度平衡的二叉树),因此查找,插入和删除是O(log 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;

}