"VBAのリスト"としましたが、使うなら配列か?コレクションか?
と言う事です。
多くの先人の方々が、実測し答えも出ているような気もしますが、
githubで公開されているものや自作も含め、そろそろ自分の中での
答えを出したい気持ちがあります。
(配列とコレクションであれば、一時的にコレクションを使って
最終的には動的配列を使う が僕の中の答えです)
まず、処理時間を測定する為のStopWatchクラスです。
'vba StopWatch.cls Option Explicit Private startTime As Double Private splitTime As Double Private Sub Class_Initialize() startTime = -1 splitTime = -1 End Sub Public Sub start() startTime = Timer splitTime = startTime End Sub Public Function total( _ Optional ByVal msg As String = "") As Double Dim t As Double t = Timer - startTime total = t Call dump_time(t, msg) End Function Public Function split(Optional ByVal msg As String = "") Dim t As Double t = Timer - splitTime split = t Call dump_time(t, msg) splitTime = Timer End Function Private Function is_started() As Boolean is_started = IIf(startTime > 0, True, False) End Function Private Sub dump_time( _ ByVal timeVal As Double, _ Optional ByVal msg As String = "") If Not is_started Then dump "---" Exit Sub End If dump msg & timeVal & "s" End Sub Private Sub dump(ByVal msg As String) Debug.Print msg End Sub
startメソッドで測定開始し、totalでstartからの時間を出力します。
splitは前回にsplitからの時間を出力し、最初のsplitの時はstartからの
時間の出力です。
resetを用意すべきかどうか悩みましたが、もう一度インスタンス
すれば済む話なので、ありません。
念の為Long型とObject型の両方を試しておきたい為、処理で
利用するだけの無意味なクラスです。
'vba myObj.cls Option Explicit Public x As Long
何も無いです。ほんの気持ちです。
続いてエントリーポイントとなるメインの関数(プロシージャ?)です。
'vba main.bas Option Explicit Dim sw_ As StopWatch Sub test_list() Dim Count As Long Count = 50000 Set sw_ = New StopWatch Debug.Print vbCrLf & "**コレクション**" sw_.start Call test1(Count) sw_.total "**終了** :" End Sub
"Call test1(Count)"を書き換える、又は追加しながら実行します。
それでは、テストする関数です。
まずは無難に普通のコレクションにします。
Private Sub test1( _ ByVal Count As Long) Dim lst As Collection Set lst = New Collection Dim i As Long For i = 0 To Count lst.Add i Next sw_.split "Long 代入:" Dim x As Long For i = 1 To lst.Count x = lst(i) Next sw_.split "呼出:" sw_.split "--:" Set lst = New Collection Dim obj As myObj Set obj = New myObj For i = 0 To Count lst.Add obj Next sw_.split "Object 代入:" For i = 1 To lst.Count Set obj = lst.Item(i) Next sw_.split "呼出:" End Sub
"for eachの方が早いじゃん"と言われるのは十分承知していますが、
統一してforで処理します。
処理的には、Long型の代入と取り出しを行い、続いてオブジェクト型の
代入と取り出しを行います。実際に実行した結果の一例です。
**コレクション** Long 代入:0.0390625s 呼出:24.4453125s --:0s Object 代入:0.0703125s 呼出:24.390625s **終了** :48.96875s
LongとObjectに大きな差はありませんね。
代入に比べ呼び出しは多くの時間がかかる事がわかりました。
"コレクションは遅い"と言われる理由は、呼び出しが遅い事が
原因の様に感じます。
続いてキー付きのコレクションです。実はこの機能はかなり
後になって知りました。要はdictionary型のような扱いが
出来るようですが、代入のaddメソッドのkeyとvalueが
他の多くの言語と逆です・・・。(気持ちはわかる)
癖強めですが、処理が早いと言う事は聞いたことがあります。
Private Sub test2( _ ByVal Count As Long) Dim lst As Collection Set lst = New Collection Dim i As Long For i = 0 To Count lst.Add i, CStr(i) Next sw_.split "Long 代入:" Dim x As Variant For i = 0 To lst.Count - 1 x = lst.Item(CStr(i)) Next sw_.split "呼出:" sw_.split "--:" Set lst = New Collection Dim obj As myObj Set obj = New myObj For i = 0 To Count lst.Add obj, CStr(i) Next sw_.split "Object 代入:" For i = 0 To lst.Count - 1 Set obj = lst(CStr(i)) Next sw_.split "呼出:" End Sub
実行結果の1例がこちら
**コレクション-キー付き** Long 代入:0.890625s 呼出:0.3671875s --:0s Object 代入:0.984375s 呼出:0.359375s **終了** :2.65625s
通常のコレクションに比べ、若干代入は遅いのですが、
呼び出しは桁違いに速いです。(恐らくForだからだと思います)
ダメだ、眠い。 続きは後日に。