Seguí los consejos de Tim Williams e hice algunas pruebas de velocidad.
Para cada tipo de colección/matriz, primero agregué 100.000 objetos de la clase "SpeedTester", que era simplemente un objeto shell que contenía una variable larga (con propiedades get/set). El valor de la variable era el valor del índice de bucle (entre 1 y 100,000)
Luego hice un segundo bucle, que implicaba acceder a cada objeto en la colección/matriz y asignar el valor de propiedad largo del objeto a una nueva variable de tipo largo. Realicé 3 rondas por método, y promedié los tiempos para Y y obtuve bucles.
Los resultados son los siguientes:
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
La colección de métodos de indexado y Colección asignada implicaba la realización de los objetos de una colección. El primero se agregó sin clave, el segundo se agregó con una clave que era la propiedad larga del objeto convertida en una cadena. A continuación, se accedió a estos objetos en un bucle for usando un índice de 1 a c.Count
Los siguientes dos métodos fueron idénticos a los dos primeros en la forma en que se agregaron las variables a la colección. Sin embargo, para el bucle Obtener, en lugar de usar un bucle for con un índice, utilicé un bucle for-each.
La matriz dinámica tipada era una clase personalizada que contenía una matriz de tipo SpeedTester. Cada vez que se agrega una variable, el tamaño de la matriz se expandió en 1 ranura (usando ReDim Preserve). El bucle de obtención era un bucle for usando un índice de 1 a 100.000, como es típico para una matriz.
Finalmente, la matriz estática tipada era simplemente una matriz de tipo SpeedTester, que se inicializó con 100.000 ranuras. Obviamente este es el método más rápido. Por extraño que parezca, gran parte de sus ganancias de velocidad fueron en Obtener en lugar de Agregar.Yo hubiera asumido que agregar sería más lento para los otros métodos, debido a la necesidad de cambiar el tamaño, mientras que Obtener cada objeto no sería más rápido que una matriz dinámica.
Me quedé asombrado por la diferencia entre usar un bucle for-loop y un bucle for-each para acceder a los objetos de una colección indexada. También me sorprendió la velocidad de búsqueda clave de la colección asignada, mucho, mucho más rápido que la indexación y comparable con todos los demás métodos, excepto la matriz estática.
En resumen, todas son alternativas viables para mi proyecto (a excepción de los métodos primero y último, primero por su lentitud, último porque necesito matrices dinámicamente redimensionables). No sé absolutamente nada sobre cómo se implementan realmente las colecciones, o las diferencias de implementación entre una matriz dinámica y estática. Cualquier idea adicional sería muy apreciada.
EDIT: El código para la prueba en sí (utilizando la matriz dinámica)
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
Y para la dinámica TesterList clase matriz:
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
Y, por último, la muy simple clase de objeto 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
¿con qué frecuencia los estás redimensionando? VBA permite el redimensionamiento dinámico de la matriz a través de 'redim preserve' si esto no ocurre a menudo y/o conoce el tamaño de la nueva matriz y le permite seguir usando una matriz (en lugar de una colección). – enderland
Si las diferencias son significativas probablemente dependerá de su caso de uso exacto. Parece que sería muy fácil probarlo: ¿has hecho alguna comparación? –