通过Scripting.Dictionary / Collection对象进行迭代

时间:2012-06-20 14:44:19

标签: performance vba collections dictionary iteration

我有一个项目在过去几年里已被改变和修改过,而且从一个代码模块到下一个代码模块的非标准化。在某些情况下,我使用了Scripting.Dictionary个对象,而在其他情况下,我有一个Collection个对象。对于其中的每一个,有时会按计数(即For i = 1 to Obj.Count)进行迭代,有时会按For...Each进行迭代。

我希望尽可能多地应用相同的逻辑来使未来的变化更加无缝,但我不确定哪种方法最好。 (我相信一些特定的案例是针对一种或另一种方法制作的,但我也非常确定某些代码可以使用任何可用的方法。)

我尝试创建一个测试子,以帮助我确定哪种方法效果最好,但结果有些不一致。总的来说,似乎循环遍历Item中的每个Dictionary更快,但在某些情况下我的测试结果显示Item中每个Collection的循环更快。方差可能取决于系统在任何给定时间发生的所有其他事情。

我想知道是否有人对于哪种方法始终最快有一个确定的答案,假设循环中的所有其他方法都是相同的。或者,有什么方法可以改进我的测试子,以便在返回结果时更加一致,这样我就可以自己回答这个问题了吗?

我提出的测试代码:

Option Explicit

Sub Test_Dictionary_Iteration_Speed()

Dim Coll1 As New Collection, Coll2 As New Collection
Dim Dict1 As New Scripting.Dictionary, Dict2 As New Scripting.Dictionary, Dict3 As New Scripting.Dictionary
Dim i As Integer, j As Integer, l As Integer
Dim StartTime As Single, StopTime As Single
Dim v As Variant
Dim Obj As TestObject 'A custom Class that has only one member variable, MainVal, and no functions/subs

    For i = 0 To 32766
        Set Obj = New TestObject
        Obj.MainVal = i
        Dict1.Add CStr(i), Obj
        Dict2.Add CStr(i), Obj
        Dict3.Add CStr(i), Obj
        Coll1.Add Obj, CStr(i)
        Coll2.Add Obj, CStr(i)
    Next i

    StartTime = Timer()

    For j = 0 To Dict1.Count - 1
        l = CInt(Dict1(CStr(j)).MainVal)
        Set Obj = Dict1(CStr(l)) 'Do something useful within the loop
        Set Obj = Nothing
        Dict1.Remove CStr(l)
    Next j

    StopTime = Timer()

    Debug.Print "Dict1 for x to y: " & StopTime - StartTime

    StartTime = Timer()

    For Each v In Dict2.Items
        l = CInt(v.MainVal)
        Set Obj = Dict2(CStr(l))
        Set Obj = Nothing
        Dict2.Remove CStr(l)
    Next v

    StopTime = Timer()

    Debug.Print "Dict2 for each item: " & StopTime - StartTime

    StartTime = Timer()

    For Each v In Dict3.Keys
        l = CInt(Dict3(v).MainVal)
        Set Obj = Dict3(CStr(l))
        Set Obj = Nothing
        Dict3.Remove CStr(l)
    Next v

    StopTime = Timer()

    Debug.Print "Dict3 for each key: " & StopTime - StartTime

    '---------- Division between Dictionary and Collection

    StartTime = Timer()

    For j = 0 To Coll1.Count - 1
        l = CInt(Coll1(CStr(j)).MainVal)
        Set Obj = Coll1(CStr(l))
        Set Obj = Nothing
        Coll1.Remove CStr(l)
    Next j

    StopTime = Timer()

    Debug.Print "Coll1 for x to y: " & StopTime - StartTime

    StartTime = Timer()

    For Each v In Coll2
        l = CInt(v.MainVal)
        Set Obj = Coll2(CStr(l))
        Set Obj = Nothing
        Coll2.Remove CStr(l)
    Next v

    StopTime = Timer()

    Debug.Print "Coll2 for each item: " & StopTime - StartTime

    Debug.Print vbNewLine & "-----" & vbNewLine   


End Sub

输出的真实示例,表明“最佳”选项并不总是相同:

  

x到y的Dict1:0.2011719
  每个项目的Dict2:0.1738281
  每个键的Dict3:0.2167969
  Coll1表示x到y:0.2050781
  每个项目的Coll2: 0.1386719

     
     

x到y的Dict1:0.1875
  每个项目的Dict2:0.171875
  每个键的Dict3:0.234375
  Coll1表示x到y:0.2050781
  每个项目的Coll2: 0.1542969

     
     

Dict1表示x到y:0.25
  每个项目的Dict2:0.21875
  每个键的Dict3:0.265625
  Coll1表示x到y:0.234375
  每个项目的Coll2: 0.171875

     
     

Dict1表示x到y:0.265625
  每个项目的Dict2: 0.203125
  每个键的Dict3:0.296875
  Coll1表示x到y:0.234375
  每个项目的Coll2:0.21875

     
     

Dict1表示x到y:0.265625
  每个项目的Dict2:0.1875
  每个键的Dict3:0.234375
  Coll1表示x到y:0.203125
  每个项目的Coll2: 0.15625

     
     

x到y的Dict1:0.28125
  每个项目的Dict2: 0.1875
  每个键的Dict3:0.25
  Coll1表示x到y:0.234375
  每个项目的Coll2: 0.1875

     
     

x到y的Dict1:0.28125
  每个项目的Dict2: 0.21875
  每个键的Dict3:0.328125
  Coll1表示x到y:0.234375
  每个项目的Coll2:0.234375

1 个答案:

答案 0 :(得分:2)

除非您知道事实某个语句或过程的执行时间是个问题,否则您不应该浪费您的人力时间进行优化。首先进行设计和调试,然后如果你认为事情太慢(它们可能不会),配置文件,然后才进行优化(执行时间通常会浪费在与你想象的完全不同的地方)。

For Each构造很简洁,整洁。不使用它的唯一原因是,如果您要从循环的集合中删除项目。然后你冒险跳过某些项目。如果您打算删除项目,请向后循环索引。