【问题标题】:Remove foreach - c# code-optimization删除 foreach - c# 代码优化
【发布时间】:2011-04-06 05:17:09
【问题描述】:

如何优化这段代码?

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 会将所有子元素和叶子元素与计数一起带来,而 ChildDogList 将具有父 ID 和叶子 ID,因此我可以将相应的子元素填充到它们的父元素并直接绑定叶子。

Parent、Child 和 Leaf Dog 将保存在一个表中,并通过 statusid 进行区分,而 count 将在另一个表中。

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

表架构:

【问题讨论】:

  • DogTypeList 是所有狗类型的列表,ParentDogTypeList 是狗类型的子集吗?
  • 这些列表是否来自数据库?
  • @Sam Saffron 是的,这两个列表都来自 DB。
  • @Preet Sangha 我有超过 3 个相同类型的内部 foreach,所以我想减少 foreach
  • @Sri,那么您能否在此处编辑问题,包括您的表的数据库架构。直接查询可能最快

标签: c# foreach ilist optimization


【解决方案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 的方式

【讨论】:

  • 那不是还有隐式嵌套循环吗?
  • 这仍然是 O(n^2) ;-( 只有 ChildDoglist 容器中的惰性 foreach 没有任何优化。
【解决方案2】:

您可以用一个简单的 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));
}

【讨论】:

    【解决方案3】:

    我仍然认为最优雅和优化的方式是使用 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());
    

    【讨论】:

      【解决方案4】:

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

      重新设计它,这样您就可以通过一次选择来获取一个查询中的所有数据。

      Albin 提到了 AddRange,但您甚至可以更进一步并虚拟化您的网格,以便它仅在用户查看网格的该部分时拉出显示给用户的行。

      编辑

      要生成您的列表,您似乎需要从数据库返回类似的内容:

      父 1,空,空 父 1、子 1、110 父 1、子 12、12 父 2,空,空 父 2、子 21、23 父 2,子 22 ,20 Leaf1,空,20 Leaf2,空,34

      这看起来你需要某种left join 和一个count,还有一个潜在的union all

      【讨论】:

      • 编辑了我的问题以解释完整的场景
      • 是的,但是我如何在没有任何循环的情况下填充列表框?
      • @Sri,一个查询和一个循环,如果没有您正在使用的表架构和 SQL 风格(假设 SQL Server),我将无法再提供帮助
      • 更新了我使用 Mysql 的架构
      • 我使用了两个查询,一个会获取叶子和子节点的计数。另一个用于获取有助于找到哪个孩子属于哪个父母的类别!
      【解决方案5】:

      慢的不是 foreach,而是添加和渲染新项目。

      添加开始更新/结束更新:

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

      【讨论】:

        【解决方案6】:

        您最大的问题可能是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()

        【讨论】:

        • 同意 AddRange 可能会有很大帮助。
        【解决方案7】:

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

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

        如果您仅运行一次此代码,您的算法是最佳。 但是,如果您正在搜索 MORE THAN ONE TIME,最佳解决方案是对容器进行排序并以线性时间进行比较,但如果您的容器可以更改,则启动搜索功能并且您使用“不止一次时间解决方案”您必须使用 RB-Tree 容器来承载此元素,因为在容器更改后使用普通列表您无法在O(log(n))时间返回排序状态。

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2012-11-13
          • 1970-01-01
          • 2013-01-17
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2020-02-16
          • 1970-01-01
          相关资源
          最近更新 更多