Ответ 1
Я последовал совету Тима Уильямса и сделал некоторые тесты скорости.
Для каждого типа коллекции/массива я впервые добавил 100 000 объектов класса "SpeedTester", который был просто объектом оболочки с длинной переменной (с настройками get/set). Значение переменной было значением индекса цикла (от 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 участвуют объекты, находящиеся в коллекции. Первые были добавлены без ключа, второй был добавлен ключом, который был объектом long property, преобразованным в строку. Затем эти объекты были доступны в цикле for, используя индекс от 1 до c.Count
Следующие два метода были идентичны первым двум способам добавления переменных в коллекцию. Однако для цикла Get вместо использования for-loop с индексом я использовал цикл for-each.
Типом динамического массива был пользовательский класс, содержащий массив типа SpeedTester. Каждый раз, когда добавляется переменная, размер массива расширяется на 1 слот (с использованием ReDim Preserve). Get-loop был для цикла с индексом от 1 до 100 000, что характерно для массива.
Наконец, типизированный массив массивов был просто массивом типа SpeedTester, который был инициализирован 100 000 слотами. Очевидно, что это самый быстрый метод. Как ни странно, большая часть его роста скорости была в получении, а не в добавлении. Я бы предположил, что добавление будет медленнее для других методов из-за необходимости изменения размера, в то время как получение каждого объекта будет не быстрее, чем динамический массив.
Меня поразила разница между использованием цикла for-loop и for for each для доступа к индексированным объектам коллекции. Я также был удивлен отображенной скоростью поиска ключей ключей - намного быстрее, чем индексирование и сопоставим со всеми другими методами, кроме статического массива.
Короче говоря, все они являются жизнеспособными альтернативами для моего проекта (за исключением первого и последнего методов, сначала из-за его медленности, потому что мне нужны динамически изменяемые размеры массивов). Я абсолютно ничего не знаю о том, как фактически реализованы коллекции, или различиях в реализации между динамическим и статическим массивом. Любое дальнейшее понимание было бы высоко оценено.
EDIT: Код самого теста (с использованием динамического массива)
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