我听从了蒂姆·威廉姆斯的建议,做了一些速度测试。
对于每种类型的集合/数组,我首先添加了 100,000 个“SpeedTester”类对象,它只是一个包含长变量(具有 get/set 属性)的 shell 对象。变量的值是循环索引的值(1 到 100,000 之间)
然后我进行了第二个循环,其中涉及访问集合/数组中的每个对象并将对象的 long 属性值分配给 long 类型的新变量。我对每种方法执行了 3 轮,并平均了 And 和 get 循环的时间。
结果如下:
Method Avg Add Time Avg Get Time Total Time
Collection Indexed 0.305 25.498 25.803
Collection Mapped 1.021 0.320 1.342
Collection Indexed For Each 0.334 0.033 0.367
Collection Mapped For Each 1.084 0.039 1.123
Dynamic Array Typed 0.303 0.039 0.342
Static Array Typed 0.251 0.016 0.266
Collection Indexed 和 Collection Mapped 方法涉及将对象保存在集合中。第一个添加没有键,第二个添加了一个键,该键是对象的长属性转换为字符串。然后使用从 1 到 c.Count 的索引在 for 循环中访问这些对象
接下来的两种方法在将变量添加到集合中的方式上与前两种方法相同。但是,对于 Get 循环,我没有使用带索引的 for 循环,而是使用了 for-each 循环。
类型化的动态数组是一个包含 SpeedTester 类型数组的自定义类。每次添加变量时,数组的大小都会扩展 1 个插槽(使用 ReDim Preserve)。get 循环是一个使用从 1 到 100,000 的索引的 for 循环,这对于数组来说是典型的。
最后,静态数组 typed 只是一个 SpeedTester 类型的数组,它被初始化为 100,000 个插槽。显然这是最快的方法。奇怪的是,它的大部分速度提升是在获取而不是添加。由于需要调整大小,我会假设其他方法的添加速度会更慢,而获取每个对象不会比动态数组快。
我对使用 for 循环和 for-each 循环访问索引集合的对象之间的区别感到震惊。映射集合的键查找速度也让我感到惊讶 - 比索引快得多,并且与静态数组以外的所有其他方法相当。
简而言之,它们都是我项目的可行替代方案(除了第一种和最后一种方法,首先是因为它的速度很慢,最后是因为我需要动态调整大小的数组)。我对集合的实际实现方式或动态数组和静态数组之间的实现差异一无所知。任何进一步的见解将不胜感激。
编辑:测试本身的代码(使用动态数组)
Public Sub TestSpeed()
Dim ts As Double
ts = Timer()
Dim c As TesterList
Set c = New TesterList
Dim aTester As SpeedTester
Dim i As Long
For i = 1 To 100000
Set aTester = New SpeedTester
aTester.Number = i
Call c.Add(aTester)
Next i
Dim taa As Double
taa = Timer()
For i = c.FirstIndex To c.LastIndex
Set aTester = c.Item(i)
Dim n As Long
n = aTester.Number
Next i
Dim tag As Double
tag = Timer()
MsgBox "Time to add: " & (taa - ts) & vbNewLine & "Time to get: " & (tag - taa)
End Sub
对于动态数组类TesterList:
Private fTesters() As SpeedTester
Public Property Get FirstIndex() As Long
On Error GoTo Leave
FirstIndex = LBound(fTesters)
Leave:
On Error GoTo 0
End Property
Public Property Get LastIndex() As Long
On Error GoTo Leave
LastIndex = UBound(fTesters)
Leave:
On Error GoTo 0
End Property
Public Sub Add(pTester As SpeedTester)
On Error Resume Next
ReDim Preserve fTesters(1 To UBound(fTesters) + 1) As SpeedTester
If Err.Number <> 0 Then
ReDim fTesters(1 To 1) As SpeedTester
End If
Set fTesters(UBound(fTesters)) = pTester
On Error GoTo 0
End Sub
Public Function Item(i As Long) As SpeedTester
On Error GoTo Leave
Set Item = fTesters(i)
Leave:
On Error GoTo 0
End Function
最后,非常简单的 SpeedTester 对象类:
Private fNumber As Long
Public Property Get Number() As Long
Number = fNumber
End Property
Public Property Let Number(pNumber As Long)
fNumber = pNumber
End Property