2012年6月28日木曜日

[ExcelVBA]コレクションオブジェクトをループさせる際の注意



結論:コレクションオブジェクトからループで値を取り出す際にFor文を使ってはいけない。


コレクションオブジェクトに値を代入し終える
時間と取り出す際の時間を計測してみた。
比較用に配列でも同じ事をしてみた。
サンプルコード
Option Explicit
Declare Function GetTickCount Lib "kernel32" () As Long

Sub SampleCode()
    Dim Temp As Long
    Dim BaseTime As Double, i As Long
    Dim MaxNumber As Long: MaxNumber = 1000
    Dim VarCollection As New Collection
    Dim VarArray() As Long
    
    '■コレクションオブジェクトに値を格納する
    BaseTime = GetTickCount
    For i = 0 To MaxNumber
        VarCollection.Add i
    Next
    Debug.Print "Collection[In] :" & GetTickCount - BaseTime
    
    BaseTime = GetTickCount
    For i = 1 To MaxNumber + 1
        Temp = VarCollection.Item(i)
    Next
    Debug.Print "Collection[In] :" & GetTickCount - BaseTime
    
    
    '■配列に値を格納する
    BaseTime = GetTickCount
    For i = 0 To MaxNumber
        ReDim Preserve VarArray(i)
        VarArray(i) = i
    Next
    Debug.Print "Collection[In] :" & GetTickCount - BaseTime
        
    BaseTime = GetTickCount
    For i = 0 To MaxNumber
        Temp = VarArray(i)
        Temp = VarArray(i)
    Next
    Debug.Print "Collection[In] :" & GetTickCount - BaseTime
End Sub

1000~5万データで各データ10回テストした平均の時間を記載してあります。
単位は[ms]です。


たった1回でもコレクションオブジェクトに対しインデックスを
使って値を取り出しただけでこれほどの時間がかかるわけです。
例えば、1つのループ内で3回、インデックス経由で呼出を行うと
3倍時間がかかるわけです。

ただし、問題はコレクションオブジェクトに対し、インデックスを利用して
値を取り出すことにあって、キー経由で取り出す分には影響がないということです。

上記コードで、コレクションオブジェクトの値を取り出す部分を
For文からFor Each文に切り替えると以下の通りになります。
単位は[ms]です。


サンプルコード
Option Explicit
Declare Function GetTickCount Lib "kernel32" () As Long

Sub SampleCode2()
    Dim Temp As Long
    Dim BaseTime As Double, i As Long, v As Variant
    Dim MaxNumber As Long: MaxNumber = 50000
    Dim VarCollection As New Collection
    
    '■コレクションオブジェクトに値を格納する
    BaseTime = GetTickCount
    For i = 0 To MaxNumber
        VarCollection.Add i
    Next
    Debug.Print "Collection[In] :" & GetTickCount - BaseTime
    
    BaseTime = GetTickCount
    For Each v In VarCollection
        Temp = v
    Next
    Debug.Print "Collection[Out]:" & GetTickCount - BaseTime
End Sub
つまり、コレクションオブジェクトはFor Eachで取り
出したほうがパフォーマンスがよいってことですね。


0 件のコメント: