こちらの続きです。
VBAのリスト問題2 - C#ATIA
あぁ配列試す前に、書くべきでした・・・・。
ところで、お気付きでしょうか?(何が?)
コレクションの結果(やる度に結果は変わります)のLong型を
扱ったものですが、
**コレクション-キー無し** Long 代入:0.0390625s 呼出:24.4453125s **コレクション-キー付き** Long 代入:0.890625s 呼出:0.3671875s
キー無しは代入に比べ、呼び出しが非常に遅いのですが、
キー付きは呼び出しの方が、代入より速いです。
逆転しています。
ここからは想像です!
何処かにメモリ確保の仕方とかの説明があれば良いのですが、
見つけられませんでした。(きっと何処かには有ると思います)
僕の中ではキー無しの場合は、こんな感じのイメージを持っています。
んーセンス無いな。 まばらに描いているのはメモリ上のアチコチに
散らばっているイメージです。
各Itemは、データの中身(本当は中身のアドレスだと思う)と次の
インデックスがセットになって情報を持っているような気がします。
キー付きの場合は、こんなイメージです。
各データはバラバラに配置されているのですが、キーについては
別の所に連続しておいてあるような気がします。
では確認。
こんな感じでサンプルを作りました。
もう、小出しでは無くドバっと。
'vba Option Explicit Private sw_ As StopWatch Sub collection_key_test() Dim Count As Long Count = 30000 'コレクション作成 Dim lst As Collection Set lst = create_collection(Count, False) 'キー無し Set sw_ = New StopWatch sw_.start Debug.Print "--キー無し--" Call non_key_test(lst) Call sw_.total("Total" & ":") 'コレクション作成 Set lst = create_collection(Count, True) 'キー付き sw_.start Debug.Print "--キー付き--" Call use_key_test(lst) Call sw_.total("Total" & ":") End Sub Private Sub use_key_test( _ ByVal lst As Collection) Dim i As Long, v As Long For i = 0 To lst.Count - 1 v = lst.Item(CStr(i)) If i Mod 1000 = 0 Then Call sw_.split(CStr(i) & ":") End If Next End Sub Private Sub non_key_test( _ ByVal lst As Collection) Dim i As Long, v As Long For i = 1 To lst.Count v = lst(i) If i Mod 1000 = 0 Then Call sw_.split(CStr(i) & ":") End If Next End Sub
それぞれ30000回Forを行い、1000回毎に時間を書き出します。
比較しやすいように、した結果がこちらです。
キー付きは最初から最後まで、ほぼ一定の時間で読み出せていますが、
キー無しは後半になる程、遅くなっています。
(For Eachも最初から最後まで、ほぼ一定でした)
この結果からすると、上で示した画像のイメージに近い事が分かります。
つまり30000個目にアクセスする為には、29999個目まで辿らないと
30000個目を見つける事が出来ない と言う事です。
じゃぁ試しに、step -1でforしたらどうなるのか?
non_key_test関数のみを書き換えます。
Private Sub non_key_test( _ ByVal lst As Collection) Dim i As Long, v As Long ' For i = 1 To lst.Count For i = lst.Count To 1 Step -1 v = lst(i) If i Mod 1000 = 0 Then Call sw_.split(CStr(i) & ":") End If Next End Sub
実行結果はこんな感じです。
--キー無し-- 30000:0.014892578125s 29000:0.656005859375s 28000:0.630126953125s 27000:0.629150390625s 26000:0.590087890625s 25000:0.56396484375s ・・・ 6000:0.14111328125s 5000:0.113037109375s 4000:0.090087890625s 3000:0.06884765625s 2000:0.049072265625s 1000:0.030029296875s Total:10.10498046875s
Forの後半は、前半より早くなってきています。
恐らくイメージ通りです。
じゃ、For EachがIndexが大きくなっても、遅くならないのか?
Forの場合、ここでListの要素が代入されています。
lstにとって、インデックス "i" が幾つが来るのかが分かっていない
のだろうと思います。
それに対しFor Eachは、ここで直接中身が取り出されています。
その為、次の要素が何かが分かっているような気がします。
(それは、イテレータだと思う)
・・・眠い。