【问题标题】:Where().Count() vs. Count()Where().Count() 与 Count()
【发布时间】:2014-02-10 17:21:48
【问题描述】:

这是我的类中继承自List(Of T) 的相关部分的摘录。查看SelectedObjectsSelectedCount 属性。出于性能原因,我添加了SelectedCount,但现在想知道它是否比SelectedObjects.Count() 更好。

Public Class DrawingObjectsList
    Inherits List(Of DrawingObjectBase)

  Friend ReadOnly Property DOsOfCurrentFace As IEnumerable(Of DrawingObjectBase)
    Get
        Return Me.Where(Function(DObj) DObj.Face = mFace)
    End Get
  End Property

  Public ReadOnly Property SelectedObjects() As IEnumerable(Of DrawingObjectBase)
    Get
        Return DOsOfCurrentFace.Where(Function(DObj) DObj.IsSelected = True)
    End Get
  End Property

  Public ReadOnly Property SelectedCount() As Integer
    Get
        Return DOsOfCurrentFace.Count(Function(DObj) DObj.IsSelected = True)
    End Get
  End Property
End Class

我的理论是Where(predicate).Count()Count(predicate) 都需要迭代列表,因此不应该有任何显着差异。另外,由于我没有在Where() 上执行任何ToList()ToArray(),我不确定在其上调用Count() 是否能够利用内置的Count 属性。

那么我应该保留还是删除SelectedCount 属性?

【问题讨论】:

  • 如果您出于性能原因做某事是否值得,有一个显而易见的方法可以找出:衡量它。
  • @JonSkeet:正确,但我也想让专业人士评估我的理论。
  • 那么您为什么不现在进行测量,并将其添加到问题中......以及尝试计算Count所花费的时间实际上在您的应用中非常重要。

标签: vb.net performance linq list


【解决方案1】:

我用一些(相当幼稚的)代码来计时:

Dim r = New Random()
Dim sw As Stopwatch
Dim list = Enumerable.Range(0, 100000000).Select(Function(x) r.Next)

sw = Stopwatch.StartNew()
list.Count(Function(x) x Mod 2 = 0)
Console.WriteLine(sw.ElapsedMilliseconds)

sw = Stopwatch.StartNew()
Dim x = list.Where(Function(x) x Mod 2 = 0).Count()
Console.WriteLine(sw.ElapsedMilliseconds)

我得到的结果是

Count   Where/Count
-------------------
3494    3624
3484    3612
3523    3617
3522    3609
3500    3623
3493    3631
3536    3620
3541    3682
3621    3633
3515    3686   

平均而言,使用Where/Count 花费的时间大约延长了 4%(在这种情况下,平均延长了 110 毫秒)。因此,可能只使用Count 会带来性能优势。但是,您首先要确认在您的特定场景/环境中是否成立。看看 Eric Lippert 的 C# Performance Benchmark Mistakes,了解如何做到这一点的好技巧。

【讨论】:

    【解决方案2】:

    正如 Jon Skeet 在 cmets 中所说,找出答案的最佳方法是测量。 但是,如果您使用SelectedObjects,我会删除CountObjects。原因如下:

    • 如果您的IEnumerable 是内存列表,那么正如您提到的CountObjects 将再次遍历原始列表。如果您已经调用了SelectedObjects 并将结果保存在一个变量中,那么调用Count 只会调用列表中的一个属性并立即为您提供对象的数量,而无需再次循环它。如果您还没有调用SelectedObjects,那么我认为调用.Where(...).Count() 并不.Count(...)。这是你应该测试的东西。

    • 如果您的 IEnumerableIQueryable,那么要么它已经实现,在这种情况下,它并不重要,或者 - 如果它没有实现 - 那么我希望调用 @987654332 @ 将转换为与 .Count(...) 相同的 SELECT COUNT(*) 查询。同样,这是你应该测试的。

    【讨论】:

    • IQueryable 的情况下,重要的是要意识到.Where 方法和.Count 方法在这些方面非常不同。依赖继承/多态会导致 .net 加载您的表,然后计算对象。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-30
    • 1970-01-01
    • 2019-05-12
    • 1970-01-01
    • 2016-04-04
    • 2011-11-25
    • 1970-01-01
    相关资源
    最近更新 更多