C#ATIA

↑タイトル詐欺 主にFusion360API 偶にCATIA V5 VBA(絶賛ネタ切れ中)

ArrayListラッパークラスをお借りしてみる

線形4分木ですが、どの様な形で返そうか? 空間の再分割が
つらいのでやめようか 等があり、ナカナカ進みません。

もう一つ悩んでいるのが、VBAのコレクション。今更ですがコレクションの
Itemがプロパティではなく、メソッドになっていることに嫌気がさして
きたので他のものを使うことにします。

手頃そうなものを探したところ、こちらを見付けました。
AarrayListをvbaで使いやすいようにラップしてみた : 趣味のプログラムあれこれ

DotNetFrameworkのArrayListのラッパークラスです。
折角なのでテストしてみました。
(クラスの名称が長かった為、cListとしています)

'vba test_ArrayList using-'KCL0.09'
'.NETFramework_ArrayList_Rapper_Class Test
'http://blog.livedoor.jp/midorityo/archives/50749809.html
Sub CATMain()
    Dim Lst As cList: Set Lst = New cList
    With Lst
        .Add 1
        .Add 10
        .Add 21
        .Add 21
        .Add -5
        .Add 3
    End With
    Call DumpList(Lst)
    
    'インデクサ NG
    'Debug.Print lst(1)
    
    '代入
    Lst.Item(0) = 100: Call DumpList(Lst)
    
    'Sort
    Call Lst.Sort: Call DumpList(Lst)
    
    'Reverse
    Call Lst.Reverse: Call DumpList(Lst)
    
    'BinarySearch
    Call DumpValue(Lst.BinarySearch(21), "Idx:")
    Call DumpValue(Lst.BinarySearch(2), "Idx:")
    
    'IndexOf
    Call DumpValue(Lst.IndexOf(21), "IndexOf:")
    Call DumpValue(Lst.IndexOf(2), "IndexOf:")
    
    'LastIndexOf
    Call DumpValue(Lst.LastIndexOf(21), "LastIndexOf:")
    Call DumpValue(Lst.LastIndexOf(2), "LastIndexOf:")
    
    'Contains
    Call DumpValue(Lst.Contains(21), "Contains:")
    Call DumpValue(Lst.Contains(2), "Contains:")
    
    'Insert
    Call Lst.Insert(1, 99): Call DumpList(Lst)
    
    'ToString
    Call DumpValue(Lst.ToString, "ToString")
    
    'TrinToSize
    Call DumpValue(Lst.capacity, "TrinToSize_Before:")
    Call Lst.TrinToSize
    Call DumpValue(Lst.capacity, "TrinToSize_After:")
    
    'Clear
    Call Lst.Clear: Call DumpList(Lst)
    
    'capacity
    Set Lst = New cList
    Call KCL.SW_Start
    Call DumpValue(Lst.capacity, "non_capacity:")
    Set Lst = InitRngList(Lst, 32766)
    Call DumpValue(KCL.SW_GetTime, , "s")
    
    Set Lst = New cList
    Call KCL.SW_Start
    Lst.capacity = 32767
    Call DumpValue(Lst.capacity, "use_capacity:")
    Set Lst = InitRngList(Lst, 32766)
    Call DumpValue(KCL.SW_GetTime, , "s")
End Sub

Private Function InitRngList(ByVal Lst As cList, ByVal count&) As cList
    Dim i&
    For i = 0 To count
        Lst.Add i
    Next
    Set InitRngList = Lst
End Function

Private Sub DumpList(ByVal Lst As cList)
    Debug.Print "---"
    For i = 0 To Lst.count - 1
        Debug.Print Lst.Item(i)
    Next
End Sub

Private Sub DumpValue(ByVal v, _
                      Optional ByVal msg_s$ = vbNullString, _
                      Optional ByVal msg_e$ = vbNullString)
    Debug.Print "---"
    Debug.Print msg_s & v & msg_e
End Sub

内容的には意味は無いのです。インデクサが無いのとForEach出来ない
部分がちょっと物足りないのですが、それ以上に不安を感じるのが
capacityの値が32767以上ではオーバーフローになってしまい、少なすぎる
気がしてます。(要素をAdd出来る最大数です)

"ForEach出来ない" 部分は、こちらを参考に修正してみましたがNG
でした。VBAのコレクションのみのお話なのでしょう。
(ArrayListクラスには、NewEnumがありません)

VBA 自作のCollectionクラスをFor Eachでまわす裏ワザ - t-hom’s diary

単純に考え、ArrayListを返すための "ToList" メソッドをクラスモジュールに
追記しました。

Function ToList() As Variant
    Set ToList = arraylist
End Function

これであれば、こんな感じでForEach出来ます。

Private Sub DumpList(ByVal Lst As cList)
    Debug.Print "---"
    For Each v In Lst.ToList
        Debug.Print v
    Next
End Sub

そんな事を試しているうちに、こちらの記述を見つけました。
VBAのコレクションへの添え字によるアクセスを早くする - Qiita
ん~VBAコレクションのキー付き(辞書型っぽい使い方)の呼び出しが
想像以上に速い。知らなかったです。
DotNetFrameworkのArrayListが遅いのは、C#をやっていた頃に
知りました。(現在はジェネリックコレクションがある為、基本ArrayList
利用されていないはず)

で、気を取り直し試してみました。

Sub CATMain2()
    Dim Lst As cList: Set Lst = New cList

    Set Lst = New cList
    Call KCL.SW_Start
    Lst.capacity = 32767
    Set Lst = InitRngList(Lst, 32766)
    
    '--loop--
    'For
    Call KCL.SW_Start
    For i = 0 To Lst.count - 1: dmy = Lst(i): Next
    Call DumpValue(KCL.SW_GetTime, "For: ", "s")
    
    'ToArray_For
    Call KCL.SW_Start
    Ary = Lst.ToArray
    For i = 0 To UBound(Ary) - 1: dmy = Ary(i): Next
    Call DumpValue(KCL.SW_GetTime, "ToArray_For: ", "s")
    
    'ForEach
    Call KCL.SW_Start
    For Each v In Lst.ToList: dmy = v: Next
    Call DumpValue(KCL.SW_GetTime, "ForEach: ", "s")
    
    'ToList_ForEach
    Call KCL.SW_Start
    Set L = Lst.ToList
    For Each v In L: dmy = v: Next
    Call DumpValue(KCL.SW_GetTime, "ToList_ForEach: ", "s")
End Sub

cListを
・素の状態でFor
・ToArrayで代入してFor
・追記したToListをそのままForEach
・追記したToListを代入してからForEach
結果はこちら

---
For: 0.516s
---
ToArray_For: 0.003s
---
ForEach: 0.018s
---
ToList_ForEach: 0.018s

以前、"ForよりForEachの方が速い" と思っていたのですが、
VBAのコレクションをForをするのが遅いのであって、For自体は速いんですね。

capacityが小さいから、配列とコレクションと併用しようかな。
(かなりわかりにくくなりそう)