删除foreach - c#code-optimization

时间:2010-08-31 11:04:50

标签: c# foreach ilist optimization

如何优化此代码?

ParentDoglist,ChildDoglistis - Ilist。 dogListBox - 列表框

foreach (Dog ParentDog in ParentDoglist)
{
 foreach (Dog ChildDog in ChildDoglist)
 {
  if(ParentDog.StatusID==ChildDog.StatusID)
  dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key));
 }
}

修改 ParentDogTypeList,DogTypeList被重命名为ParentDoglist,ChildDoglist,两者都不相关

if(ParentDog.Key==ChildDog.Key)

已更改为

if(ParentDog.StatusID==ChildDog.StatusID)

完整故事:

我需要填充一个下拉菜单,这将回应父子关系。有些狗可能没有任何孩子,这将被称为看门狗。而且我还需要显示该特定类别的狗的数量

DD看起来像

Parent1
  Child11 (10)
  Child12 (12)
Parent2
  Child21 (23)
  Child22 (20)
Leaf1 (20)
Leaf2 (34)

因此,ParentDoglist将带来所有Child和leaf元素以及count和ChildDogList将具有Parent和leaf ID,因此我可以将相应的Child填充到其Parent并直接绑定叶子。

父母,儿童和叶子狗将保留在一个表格中,并通过statusid进行区分,计数将在另一个表格中。

没有父母会有任何计数,只有孩子和叶子会有计数

表架构:

alt text

7 个答案:

答案 0 :(得分:8)

您可以对ParentDoglistChildDoglist进行排序,并对此O(n)执行线性O(n^2)查找算法。

但您可以在O((ParentDoglist.Size() + ChildDoglist.Size()) * log2(ParentDoglist.Size() + ChildDoglist.Size()))中对容器进行排序。

然后,如果您运行此代码 ONCE ONLY ,您的算法最佳。 但是,如果您正在搜索超过一次,最佳解决方案是对容器进行排序并在线性时间内进行比较,但是如果您的容器可以更改,那么搜索功能就会被改变并且您使用“不止一次”时间解决方案“您必须使用 RB-Tree 容器来携带此元素,因为在容器更改后的正常列表中,您无法在O(log(n))时间内返回到已排序状态。

答案 1 :(得分:2)

你最大的问题可能是dogListBox.Items.Add。一次添加一个项目非常昂贵。 ListBox.Items.AddRange效率更高。

要使内循环更小,可以在内循环中为键创建查找。

List<ListItem> listItems = new List<ListItem>();
ILookup<string, Dog> childDogsPerKey = ChildDoglist.ToLookup(dog => dog.Key);
foreach (Dog ParentDog in ParentDoglist)
{
    foreach (Dog ChildDog in childDogsPerKey[ParentDog.Key])
    {
        listItems.Add(new ListItem(ParentDog.Name, ParentDog.Key));
    }
}
dogListBox.Items.AddRange(listItems.ToArray());

此代码假设有几只幼犬可以拥有相同的密钥。如果每个键只能有一只幼犬,则可以使用.ToDictionary()代替

答案 2 :(得分:2)

我仍然认为最优雅和最优化的方法是使用Linq。

box.Items.AddRange(
   ParentDoglist.Where(p=>ChildDoglist.Any(c=>c.StatusID== p.StatusID))
    .Select(r=>new ListItem(r.StatusID, r.Name)).ToArray());

这就是全部而且只有一条线。 如果您更喜欢连接,则可以使用该查询来完成。

box.Items.AddRange(
   ParentDoglist.Join(ChildDoglist, p => p.StatusID, c => c.StatusID, (p,c)=>p)
    .Select(r=>new ListItem(r.StatusID, r.Name)).ToArray());

答案 3 :(得分:1)

由于这是来自数据库,数据库往返可能是性能杀手。此外,比较ParentDog.Key==ChildDog.Key可以在SQL中完成,因此您不会将所有数据提取到您的应用只是为了丢弃它。

重新设计,以便您进行一次选择以获取一个查询中的所有数据。

Albin提到了AddRange,但你甚至可以更进一步,虚拟化你的网格,这样它只会在看到网格的那一部分时拉出显示给用户的行。

修改

要生成列表,您似乎需要从DB中返回类似的内容:

Parent1, null, null
Parent1, Child1, 110
Parent1, Child12, 12
Parent2, null, null
Parent2, Child21, 23
Parent2, Child22 ,20
Leaf1, null, 20
Leaf2, null, 34

看起来您需要某种left joincount,并且可能会引入union all

答案 4 :(得分:1)

这不是一个缓慢的foreach,它正在添加和渲染新项目。

添加beginupdate / endupdate:

dogListBox.BeginUpdate();
foreach (Dog ParentDog in ParentDoglist) 
{ 
 foreach (Dog ChildDog in ChildDoglist) 
 { 
  if(ParentDog.Key==ChildDog.Key) 
  dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key)); 
 } 
} 
dogListBox.EndUpdate();

答案 5 :(得分:0)

您可以使用简单的Linq表达式替换嵌套的foreach循环。为此,您需要使用System.Linq;

foreach (Dog ParentDog in 
            (from dog in ParentDogList
             from ChildDog in dog.StatusId
             where dog.StatusId == ChildDog.StatusId)
             select dog) )
{
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key));
}

答案 6 :(得分:-1)

foreach (var ParentDog in ParentDoglist.Where(p=>ChildDoglist.Any(c=>c.Key== p.Key)).ToList())
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key));

这就是你用LinQ做的方法