私はティム・ウィリアムスのアドバイスに従い、スピードテストを行った。
コレクション/配列の種類ごとに、最初にクラス変数 "SpeedTester"の100,000個のオブジェクトを追加しました。これは単純に長い変数(get/setプロパティ付き)を保持するシェルオブジェクトでした。変数の値はループインデックスの値です(1〜100,000)
次に、コレクション/配列内の各オブジェクトにアクセスし、そのオブジェクトのlongプロパティ値を新しい変数に割り当てるという2番目のループを行いました長い型の私はメソッドごとに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
方法コレクションインデックス付きとマップされたコレクションは、コレクション内のオブジェクトを保持して関与しました。最初のキーはキーなしで追加され、2番目のキーにはオブジェクトの長いプロパティが文字列に変換されたキーが追加されました。これらのオブジェクトは、1からc.Countのインデックスを使用してforループでアクセスされました。
次の2つのメソッドは、変数がコレクションに追加される方法で最初の2つと同じでした。しかし、Getループでは、forループとインデックスを使用する代わりにfor-eachループを使用しました。
動的配列型は、SpeedTester型の配列を含むカスタムクラスでした。変数が追加されるたびに、配列のサイズが1スロット拡張されました(ReDim Preserveを使用)。 get-loopは、配列の典型的な1〜100,000のインデックスを使用するfor-loopでした。
最後に、タイプされた静的配列は単純に10000スロットで初期化されたSpeedTester型の配列でした。明らかに、これは最速の方法です。不思議にも、速度向上の多くは、追加ではなく取得であった。私は、サイズ変更の必要性のために、他のメソッドのほうが遅くなると思っていましたが、各オブジェクトを取得するのは動的配列よりも速くないでしょう。
私は、forループとfor-eachループを使用してインデックス付きコレクションのオブジェクトにアクセスすることの違いに驚きました。マッピングされたコレクションの主要なルックアップスピードにも驚いていました。インデックス作成よりはるかに高速で、静的配列以外のすべてのメソッドに匹敵しました。
要するに、それらは私のプロジェクトにとって実行可能なすべての選択肢です(最初と最後のメソッドを除いて、最初は遅いため、最後は動的にサイズ変更可能な配列が必要です)。コレクションが実際にどのように実装されているか、または動的配列と静的配列の実装の違いについては、私は全く知りません。それ以上の洞察があれば幸いです。
EDIT:
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オブジェクトクラス:(動的配列を使用して)テスト自体
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 fNumber As Long
Public Property Get Number() As Long
Number = fNumber
End Property
Public Property Let Number(pNumber As Long)
fNumber = pNumber
End Property
どのくらいの頻度でサイズを変更していますか? VBAでは、これが頻繁に起こらない場合や、新しい配列のサイズを知っていて、(コレクションではなく)配列を使い続ける場合、 'redim preserve'を使って動的配列のサイズ変更を行うことができます。 – enderland
差異が重要かどうかは、正確なユースケースによります。しかし、それは非常に簡単にテストするように思える:任意の比較を実行している? –