简体   繁体   English

删除foreach - c#code-optimization

[英]Remove foreach - c# code-optimization

How to optimize this code? 如何优化此代码?

ParentDoglist, ChildDoglistis - Ilist. ParentDoglist,ChildDoglistis - Ilist。 dogListBox - List Box 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));
 }
}

EDIT: ParentDogTypeList, DogTypeList were renamed as ParentDoglist,ChildDoglist, where both are not related with each other 编辑: ParentDogTypeList,DogTypeList被重命名为ParentDoglist,ChildDoglist,其中两者彼此无关

if(ParentDog.Key==ChildDog.Key)

was changed to 被改为

if(ParentDog.StatusID==ChildDog.StatusID)

Complete Story: 完整故事:

I need to populate a drop down which would reciprocate a Parent Child relationship. 我需要填充一个下拉菜单,这将回应父子关系。 There are certain dogs which may not have any child and that would be called as leafdog. 有些狗可能没有任何孩子,这将被称为看门狗。 And I also need to show the number of dogs in that particular category 而且我还需要显示该特定类别中的狗的数量

DD would look like DD看起来像

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

So, the ParentDoglist would bring all the Child and leaf elements along with the count and ChildDogList would have the Parent and leaf ID's hence I would be able to populate the respective Child to their Parent and bind the leaf directly. 因此,ParentDoglist将带来所有Child和leaf元素以及count和ChildDogList将具有Parent和leaf ID,因此我可以将相应的Child填充到其Parent并直接绑定叶子。

The Parent, Child and Leaf Dog would be maintain in one table and differentiated by statusid and count would be in another table. 父,子和叶狗将保留在一个表中,并通过statusid进行区分,计数将在另一个表中。

No parent would have any count, only child and leaf would have count 没有父母会有任何计数,只有孩子和叶子才算数

Table Schema: 表格架构:

替代文字

You can sort ParentDoglist and ChildDoglist and do linear O(n) finding algorithm insead of this O(n^2) . 您可以按ParentDoglistChildDoglist并做线性O(n)发现这个算法INSEAD O(n^2)

But you can sort the containers in O((ParentDoglist.Size() + ChildDoglist.Size()) * log2(ParentDoglist.Size() + ChildDoglist.Size())) . 但是您可以在O((ParentDoglist.Size() + ChildDoglist.Size()) * log2(ParentDoglist.Size() + ChildDoglist.Size()))对容器进行排序。

Then if you run this code ONCE ONLY , your algorithm is optimal . 然后,如果运行此代码,则算法是最佳的 But if you are searching MORE THAN ONE TIME , the optimal solution is sort the containers and do the comparison in linear time, but if yours container can change bettwen search functions was lanuched and you using "more than once time solution" you must use RB-Tree container to carry this elements, because with normal list after container was changed you can't return to sorted state in O(log(n)) time. 但是,如果您搜索的时间超过一次,最佳解决方案是对容器进行排序并在线性时间内进行比较,但是如果您的容器可以改变搜索功能,那么您需要使用“不止一次的解决方案”,您必须使用RB -Tree容器来携带这个元素,因为在容器被更改后的正常列表中你无法在O(log(n))时间内返回到已排序状态。

Your biggest problem is probably the dogListBox.Items.Add . 你最大的问题可能是dogListBox.Items.Add Adding each items one at a time is quite expensive. 一次添加一个项目非常昂贵。 ListBox.Items.AddRange is more efficient. ListBox.Items.AddRange更有效。

To make the inner loop much smaller you can create a lookup for the keys in the inner loop. 要使内循环更小,可以为内循环中的键创建查找。

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());

This code assumes that several child dogs can have the same key. 此代码假定几只子狗可以拥有相同的密钥。 If there can only be one child dog per key you may use .ToDictionary() instead 如果每个键只能有一个小狗,你可以使用.ToDictionary()代替

I still think the most elegant and optimized way is to use Linq for it. 我仍然认为最优雅和优化的方法是使用Linq。

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

That's all and it's only one line. 这就是全部而且只有一条线。 If you prefer joins, you can do it with that query. 如果您更喜欢连接,则可以使用该查询执行此操作。

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

Since this is coming from the DB, database round trips are likely to be a performance killer. 由于这是来自数据库,数据库往返可能是性能杀手。 Also the comparison ParentDog.Key==ChildDog.Key can be done in SQL, so you don't pull all that data to your app just to discard it. 此外,比较ParentDog.Key==ChildDog.Key可以在SQL中完成,因此您不会将所有数据提取到您的应用只是为了丢弃它。

Redesign this so you do a single select to grab all the data in one query. 重新设计此选项,以便您只需一次选择即可获取一个查询中的所有数据。

Albin mentioned AddRange, but you can even take this a step further and virtualize your grid so it only pulls the rows displayed to the user when she is looking at that portion of the grid. Albin提到了AddRange,但你甚至可以更进一步,虚拟化你的网格,这样它只会在看到网格的那一部分时拉出显示给用户的行。

EDIT 编辑

To generate your list it appears you need to return something like this from the DB: 要生成列表,您需要从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

This looks like you need some sort of left join and a count , with a potential union all thrown in. 这看起来像你需要一些left join和一个count ,一个潜在的union all被抛入。

It's not the foreach that is slow, it's adding and rendering new items. 这不是一个缓慢的foreach,它正在添加和渲染新项目。

Add beginupdate/endupdate: 添加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();

You can replace the nested foreach loop with a simple Linq expression. 您可以使用简单的Linq表达式替换嵌套的foreach循环。 For this to work you need to be using System.Linq; 为此,您需要使用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));
}
foreach (var ParentDog in ParentDoglist.Where(p=>ChildDoglist.Any(c=>c.Key== p.Key)).ToList())
    dogListBox.Items.Add(new ListItem(ParentDog.Name, ParentDog.Key));

That's how you would do it with LinQ 这就是你用LinQ做的

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM