&での文字列連結は遅い
文字列を連結する場合、通常は「a = “abc” & “cde”」のように&を使って行います。
&を使った文字列の連結処理は一般的に行われるものですが、連結結果は元のメモリ領域とは別の領域に対して行うため新しい領域確保が必要になり、その領域確保はVBAに限らずどのようなプログラミング言語でも時間が掛かる処理になります。
これが&での文字列連結に時間が掛かる理由です。連結の回数に比例してどんどん遅くなります。
Midステートメントでの書き換えは速い
&の文字列連結で時間が掛かるのは新たに領域の確保を行うことが原因のためで、言い方を変えれば、新しい領域の確保をせずに文字列の連結を行うことが出来れば、処理は速くなります。これも他のプログラミング言語でも同じです。
VBAで扱う文字列は通常は領域サイズを指定しませんが、事前に指定した領域を確保することは可能です。
ただ、&での文字列連結をすると新しい領域の確保が発生してしまうため、別の方法で文字列連結を行う必要があります。具体的には文字列連結ではなく、確保済みの領域をMidステートメントで書き換えることで実現します。
Midステートメントは既に確保済みの領域の一部の書き換えだけを行うため、領域の再確保が発生しない分、&よりも高速に処理されます。
以下のコードで、一般的な文字列連結の場合と、事前に領域を確保した場合でどれぐらいの違いがあるのかを紹介します。
一般的な文字列連結のコード
以下のコードは10万回のループで1文字ずつ文字列変数に連結しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
Sub StringCat1() Dim s As String Dim lSize Dim i lSize = 100000 i = 1 Do If (i > lSize / 2) Then Exit Do End If '// 文字列を連結 s = s & "1" i = i + 1 Loop End Sub |
事前に領域を確保して文字列連結するコード
事前に文字列のサイズを確保して10万回のループで1文字ずつ文字列変数を書き換えています。
書き換えはMidステートメントで対象位置を指定の文字に対して行います。ループ終了後に書き換えを行った部分だけを文字列変数に再設定しています。
MidステートメントはMid関数と同じ名前ですが用途が異なります。Mid関数は文字列の一部を返しますが、Midステートメントは文字列の一部を書き換えます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
Sub StringCat2() Dim s As String Dim lSize Dim i lSize = 100000 '// 指定サイズの領域を確保 s = String(lSize, 0) i = 1 Do If (i > lSize / 2) Then Exit Do End If '// 指定位置の文字列を書き換え Mid(s, i, 1) = "1" i = i + 1 Loop '// 連結した部分のみを残す s = Mid(s, 1, i - 1) End Sub |
速度比較
上記の2つのコードを実行してどれぐらいの差があるのかそれぞれ10回ずつ実行して比較します。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
Sub StringCatTest() Dim t1 Dim t2 Dim t3 Dim i For i = 1 To 10 t1 = Timer Call StringCat1 '// 通常時 t2 = Timer Call StringCat2 '// 事前確保時 t3 = Timer Debug.Print "通常時:" & CStr(t2 - t1) Debug.Print "事前確保時:" & CStr(t3 - t2) Next End Sub |
実行結果の平均は通常時は約0.16秒、事前確保時が0.006秒でした。単純比較では26倍程度の差があります。
さらに10万回ループを100万回ループで行うと、約80秒と約0.07秒の1000倍以上の差が出ました。回数に比例することがこのことからも分かります。
事前に領域を確保する方法は処理速度を要求される場合には有効な手段ですが、Midステートメントがあまり見慣れないコードのため、直観的に分かりにくいコードなのが欠点です。