2012-06-20 216 views
2

我有一个项目在过去的几年中已经发生了一些变化和修改,并且从一个代码模块到另一个代码模块非常不规范。在某些情况下,我使用了Scripting.Dictionary对象,而在其他情况下,我有一个Collection对象。对于其中的每一个,有时会按计数进行迭代(即For i = 1 to Obj.Count),有时则会由For...Each进行迭代。通过Scripting.Dictionary/Collection对象迭代

我想尽可能多地应用相同的逻辑以使未来的更改有希望更加无缝,但我不确定哪种方法最好。 (我相信一些特定的情况下做了一个或另一种方法,但我也非常确定一些代码可以使用任何可用的方法。)

我试着创建一个测试子,以帮助我确定哪个这些方法的效果最好,但结果有些不一致。总的来说,看起来循环遍历每个ItemDictionary更快,但在某些情况下,我的测试显示通过每个Item的循环更快。差异可能取决于系统在任何给定时间发生的一切。

我想知道是否有人有一个明确的答案,哪些方法一直是最快的,假设循环内的所有其他方法都是一样的。或者,有什么方法可以提高我的测试子系统在返回结果时更加一致,以便我可以自己回答这个问题?

测试代码中,我想出了:输出

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 

真实的例子,显示出 '最佳' 选项并不总是相同的:

Dict1对于x到y:0.2011719
每个项目的Dict2:0.1738281
每个键的Dict3:0.2167969
Coll1 for x to y:0.2050781
Coll2每个项目:0.1386719


Dict1为x至y: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 for x to 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


Dict1为x至y:0.28125
Dict2每个项目:0.1875
Dict3用于每个键:0.25
Coll1为x至y:0.234375
Coll2每个项目:0.1875


Dict1为x至y:0.28125
Dict2每个项目:0.21875
Dict3每个键:0.328125
Coll1为X到Y:0.234375
Coll2每个项目:0.234375

回答

2

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

For Each构造简洁而整齐。不使用它的唯一原因是如果您要删除正在循环播放的集合中的项目。那么你有可能跳过某些项目。如果您计划删除项目,请向后循环索引。

+0

据我所知,速度并不是问题,而且所有事情都按预期工作。我对改变事物的唯一兴趣是使所有模块尽可能的相似和一致。我认为,虽然我在这里,但我不妨寻找可以做到的最快捷的选择。 – Gaffi

+0

一点;你的'“Coll1 for x to y:”'使用'Coll1(CStr(j))'这是一个*键*查找,而不是索引查找,因为我认为你假设;如果你重构那段代码(并从索引'1'开始),你会得到一个非常不同的(可怕的)(可怕的) )结果(其链接列表,获取第n个项目是沿着链条跳转) - 您是专注于基于索引的查找还是关联的键? –

+0

@AlexK。这个答案取决于它被使用的时间(它可以在这个特定的项目中),但我认为在大多数情况下没有使用密钥。很多时候,因为'集合'允许在添加时有空的参数,所以后面的代码甚至不知道要查找什么键(另一点我会尽可能标准化)。 – Gaffi