c#对于大型数据集,自定义分组速度非常慢

c#对于大型数据集,自定义分组速度非常慢,c#,performance,optimization,C#,Performance,Optimization,当我运行下面的代码,其中活动.Count()为200000时,这段代码非常慢 List<Campaign> listCampaigns = new List<Campaign>(); foreach (var item in campaigns) { if (listCampaigns.Where(a => a.CampaignName == item.CampaignName && a.Ter

当我运行下面的代码,其中活动.Count()为200000时,这段代码非常慢

List<Campaign> listCampaigns = new List<Campaign>();
        foreach (var item in campaigns)
        {
            if (listCampaigns.Where(a => a.CampaignName == item.CampaignName && a.Term == item.Term).Count() == 0)
            {
                //this doesn't exist
                listCampaigns.Add(item);
            }
            else
            {
                //this exists already
                var campaign = listCampaigns.Where(a => a.CampaignName == item.CampaignName && a.Term == item.Term).First();
                campaign.TotalVisits += item.TotalVisits;
                List<Conversion> listConversions = item.Conversions.ToList();
                listConversions.AddRange(campaign.Conversions.ToList());
                campaign.Conversions = listConversions.ToArray();
            }
        }
List listCampaigns=new List();
foreach(活动中的var项目)
{
if(listCampaigns.Where(a=>a.CampaignName==item.CampaignName&&a.Term==item.Term).Count()==0)
{
//这是不存在的
列表活动。添加(项目);
}
其他的
{
//这已经存在了
var campaign=listCampaigns.Where(a=>a.campaigname==item.campaigname&&a.Term==item.Term).First();
campaign.totalvisions+=item.totalvisions;
List listConversions=item.Conversions.ToList();
listConversions.AddRange(campaign.Conversions.ToList());
campaign.Conversions=listConversions.ToArray();
}
}
是否有任何方法可以优化此代码的某些部分或使用其他方法来加速它

如有任何建议,我们将不胜感激。谢谢。

至少使用
Any()
而不是
Count()
-在这种情况下,您不必检查完整列表:

if (listCampaigns.Where(a => a.CampaignName == item.CampaignName 
                        && a.Term == item.Term).Any())
另外,正如其他人指出的那样,更快的方法是使用
字典来快速访问,您必须为每个
活动定义一个唯一的键值,然后您可以使用
字典

至少使用
Any()
而不是
Count()
-在这种情况下,您不必检查完整列表:

if (listCampaigns.Where(a => a.CampaignName == item.CampaignName 
                        && a.Term == item.Term).Any())
另外,正如其他人指出的那样,快速访问的
字典要快得多,您必须为每个
活动定义一个唯一的键值,然后您可以使用
字典
使用。您可以将活动名称和术语放入元组,并使用它在O(1)中查找现有活动。这使得整个代码为O(n)

我们当前的代码是O(n^2),因为它需要遍历整个列表以检查当前条目的存在

代码应类似于以下内容:

var dict=new Dictionary<Tuple<string,Term>,Campaign>();
var currentKey=new Tuple<string,Term>(item.CampaignName, item.Term == item.Term);
Campaign existingCampaign;
if (dict.TryGetValue(currentKey,out existingCampaign))
{
//already exists
}
else
{
//new
}
var dict=newdictionary();
var currentKey=新元组(item.campaigname,item.Term==item.Term);
运动存在运动;
if(dict.TryGetValue(currentKey,out existingCampaign))
{
//已经存在
}
其他的
{
//新的
}
使用。
字典
。您可以将活动名称和术语放入元组,并使用它在O(1)中查找现有活动。这使得整个代码为O(n)

我们当前的代码是O(n^2),因为它需要遍历整个列表以检查当前条目的存在

代码应类似于以下内容:

var dict=new Dictionary<Tuple<string,Term>,Campaign>();
var currentKey=new Tuple<string,Term>(item.CampaignName, item.Term == item.Term);
Campaign existingCampaign;
if (dict.TryGetValue(currentKey,out existingCampaign))
{
//already exists
}
else
{
//new
}
var dict=newdictionary();
var currentKey=新元组(item.campaigname,item.Term==item.Term);
运动存在运动;
if(dict.TryGetValue(currentKey,out existingCampaign))
{
//已经存在
}
其他的
{
//新的
}

在将200000个活动项目添加到主列表之前,能否避免将其转换成具体列表

我想:

  • 将Where().Count()替换为Any()函数,在一般情况下,这将更快地给出正确答案
  • 重构ToLists();这些函数将源集合克隆到一个新的集合实例中,这非常耗费时间和内存,尤其是在这样的循环中。每次迭代创建两个列表和一个数组;住手
以下是新代码:

List<Campaign> listCampaigns = new List<Campaign>();
    foreach (var item in campaigns)
    {
        if (!listCampaigns.Any(a => a.CampaignName == item.CampaignName && a.Term == item.Term))
        {
            //this doesn't exist
            listCampaigns.Add(item);
        }
        else
        {
            //this exists already
            var campaign = listCampaigns.First(a => a.CampaignName == item.CampaignName && a.Term == item.Term);
            campaign.TotalVisits += item.TotalVisits;
            //Reduces the number of collection copies created per iteration from 3 to 1
            campaign.Conversions = campaignConversions.Concat(item.Conversions).ToArray();
        }
    }
List listCampaigns=new List();
foreach(活动中的var项目)
{
如果(!listCampaigns.Any(a=>a.CampaignName==item.CampaignName&&a.Term==item.Term))
{
//这是不存在的
列表活动。添加(项目);
}
其他的
{
//这已经存在了
var-campaign=listCampaigns.First(a=>a.campaigname==item.campaigname&&a.Term==item.Term);
campaign.totalvisions+=item.totalvisions;
//将每次迭代创建的集合副本数从3个减少到1个
campaign.Conversions=campaignConversions.Concat(item.Conversions.ToArray();
}
}

在将200000个活动项目添加到主列表之前,能否避免将其转换成具体列表

我想:

  • 将Where().Count()替换为Any()函数,在一般情况下,这将更快地给出正确答案
  • 重构ToLists();这些函数将源集合克隆到一个新的集合实例中,这非常耗费时间和内存,尤其是在这样的循环中。每次迭代创建两个列表和一个数组;住手
以下是新代码:

List<Campaign> listCampaigns = new List<Campaign>();
    foreach (var item in campaigns)
    {
        if (!listCampaigns.Any(a => a.CampaignName == item.CampaignName && a.Term == item.Term))
        {
            //this doesn't exist
            listCampaigns.Add(item);
        }
        else
        {
            //this exists already
            var campaign = listCampaigns.First(a => a.CampaignName == item.CampaignName && a.Term == item.Term);
            campaign.TotalVisits += item.TotalVisits;
            //Reduces the number of collection copies created per iteration from 3 to 1
            campaign.Conversions = campaignConversions.Concat(item.Conversions).ToArray();
        }
    }
List listCampaigns=new List();
foreach(活动中的var项目)
{
如果(!listCampaigns.Any(a=>a.CampaignName==item.CampaignName&&a.Term==item.Term))
{
//这是不存在的
列表活动。添加(项目);
}
其他的
{
//这已经存在了
var-campaign=listCampaigns.First(a=>a.campaigname==item.campaigname&&a.Term==item.Term);
campaign.totalvisions+=item.totalvisions;
//将每次迭代创建的集合副本数从3个减少到1个
campaign.Conversions=campaignConversions.Concat(item.Conversions.ToArray();
}
}
使用
字典
。这样,您就可以使用哈希表来检查这些值是否存在,并在O(1)中找到相应的值

代码示例:

var dictCampaigns = new Dictionary<Key, Campaign>();
foreach (var item in campaigns)
{
    Campaign found;
    var key = new Key(item);
    if(!dictCampaigns.TryGetValue(key,out found))
    {
        dictCampaigns.Add(key, item);
    }
    else
    {
        found.TotalVisits += item.TotalVisits;
        found.Conversions = (item.Conversions.Concat(found.Conversions)).ToArray();
    }
}
我用秒表粗略地测量了一下,它比你的代码快了两倍,但我认为仍然可以优化。

使用
字典。这样,您就可以使用哈希表来检查这些值是否存在,并在O(1)中找到相应的值

代码示例:

var dictCampaigns = new Dictionary<Key, Campaign>();
foreach (var item in campaigns)
{
    Campaign found;
    var key = new Key(item);
    if(!dictCampaigns.TryGetValue(key,out found))
    {
        dictCampaigns.Add(key, item);
    }
    else
    {
        found.TotalVisits += item.TotalVisits;
        found.Conversions = (item.Conversions.Concat(found.Conversions)).ToArray();
    }
}
我用秒表粗略地测量了一下,大概有两次