【问题标题】:Repeated where() vs multiple collections in c#c# 中重复 where() 与多个集合
【发布时间】:2011-10-12 07:05:35
【问题描述】:

我有一个对象 A 的集合。每个 A 都有一个与对象 B 相关的字段 - 我有另一个集合。换句话说,每个 B 都附加到 As 集合的一个子集(但只是在概念上,而不是在代码中)。该字段(将 A 与 B 相关联)可以在系统的生命周期内发生变化。有一些系统要求阻止了这种结构的改变。

如果我需要对每个 B 的 A 集合重复执行操作,最好对 A 的集合重复使用 Where() 方法还是创建 B 拥有的另一个集合和一个管理添加和删除的类的相关项目。

让我看看我是否可以在代码中捕获它:

    class A {
      public B owner;
      ...
    }

    class B {
      ...
    }

    class FrequentlyCalledAction {
      public DoYourThing(B current) {
        List<A> relevantItems = listOfAllAItems.Where(x => x.owner == current).ToList()
        foreach (A item in relevantItems) {
          ...
        }
      }
    }

对比:

    class A {
      public B owner;
      ...
    }

    class B {
      public List<A> itsItems;
    }

    class FrequentlyCalledAction {
      public void DoYourThing(B current) {
        foreach (A item in current.itsItems) {
          ...
        }
      }
    }

    class AManager {
      public void moveItem(A item, B from, B to) {
        from.itsItems.remove(item);
        to.itsItems.add(item);
      }
    }

【问题讨论】:

  • 你多久做一次? A->B 关系多久改变一次? As 和 B 的集合有多大? B 不能引用 A 吗?
  • DoYourThing 是系统中最频繁的调用之一。 A->B 关系可能每 4 次 DoYourThing 调用改变一次。它们都是小型套装 - 可能是 50 B,也可能是 100 A。
  • A 需要多久知道一次它的所有者是谁?您能否将 A 存储在其所有者身上,并在需要时确定其所有者是谁?
  • 系统的其他部分需要 A 知道它的所有者。
  • 你最后决定做什么?

标签: c# performance coding-style


【解决方案1】:

这主要取决于集合的大小。如果只有几个项目,则解决方案 two 带来的开销大于性能增益。

在这种情况下,我会使用解决方案 one,因为它具有更好的可读性并且管理起来不太复杂。

如果集合中有数千个项目,我会选择解决方案两个moveItems 方法是一个 O(n) 操作,但在您的场景中,读取次数似乎多于写入次数。因此,您可以通过更结构化的设计获得更多性能。

【讨论】:

  • 我认为可以使用 HashSet 将 MoveItems 提高到 O(1)。
【解决方案2】:

事实上,这一切都取决于您收藏的大小。 Sol 2 更复杂,但对于大型收集更快,而 sol1 对于少于 100/1000 个左右的项目可能非常快。

【讨论】:

  • 解决方案 2 的空间使用有缺点吗?因为它们都只是参考,所以这有关系吗?性能提升是否超过它?
  • 同样,这一切都取决于集合的大小,但实际上,它只是将存储在列表中的引用。
【解决方案3】:

由于集合很小(约 100 个项目)并且它们经常更改(约每 4 次迭代),请执行此操作,然后看看您是否有问题。

public DoYourThing(B current)
{
    foreach(A item in listOfAllAItems.Where(a => a.owner == current))
    {
        ...
    }
}

我认为将 IEnumrable 转换为 IList 没有任何意义。

如果这给您带来性能问题,我认为AManager 不是您的最佳答案,尽管这可能取决于关系的变化程度。

【讨论】:

    【解决方案4】:

    如果您选择解决方案 2,则可能值得使用 HashSet 而不是 List。 HashSet 是 O(1) 添加和删除,而 List 是 O(1) 添加和 O(n) 删除。

    另一个选项是这样的,它的优点是 A 和 B 的用户不需要记住使用AManager

    static class GlobalDictionary
    {
      private static Dictionary<B,HashSet<A>> dictionary = new Dictionary<B,HashSet<A>>();
    
      public static HashSet<A> this[B obj]
      {
        // You could remove the set and have it check to see if a Set exists for a B
        // if not you would create it.
        get {return dictionary[obj];}
        set {dictionary[obj] = value;}
      }
    } 
    
    
    class A 
    {
      private B owner;  
      public B Owner
      {
        get{ return owner;}
        set
        {
          if (owner != null) GlobalDictionary[owner].Remove(this);
    
          owner = value;
          GlobalDictionary[owner].Add(this);
        }
      }
    }
    
    class B
    {
      public B()
      {
        GlobalDictionary[this] = new HashSet<A>();
      }
    
      public IEnumerable<A> Children
      {
        get
        {
          return GlobalDictionary[this];
        }
      }
    }
    

    我还没有尝试过,所以它可能需要一些调整,但你应该明白了。

    【讨论】:

    • 您可能想考虑如何在没有其他对象引用 B 时从全局字典中删除它们,以便垃圾收集器可以处理它们。
    猜你喜欢
    • 2016-04-05
    • 1970-01-01
    • 2021-11-15
    • 2023-03-06
    • 2019-02-09
    • 2017-11-02
    • 2018-02-12
    • 1970-01-01
    • 2018-09-24
    相关资源
    最近更新 更多