文字列同士の連結は遅い
あまり知られていませんが、&や+での文字列の連結処理はかなり遅い処理になります。
その理由は、連結前の文字列と、連結後の文字列が格納されるメモリ領域が異なるためです。2つ以上の文字列を連結すると、連結した文字列を格納できるメモリ領域を新たに用意します。
メモリ領域の確保はコストが大きく、ループ処理で何度も文字列の連結を行うような場合には目に見えて遅くなります。それを回避するには、メモリ領域の確保が毎回必要な「文字列の連結」処理をやめるしかありません。
以下では、文字列の連結がどれぐらい遅いのか、そして、どうやったら速くなるのかをサンプルコードで説明します。
String型の連結速度(遅い方法)
実際にどれぐらい遅いのか計測してみます。
“a”という文字を30万回連結する処理です。
処理の前後にTimer関数を呼び出し、その差から処理秒数を算出しています。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Sub stringAppendTest() Dim s As String Dim i Dim tm tm = Timer For i = 0 To 300000 s = s & "a" Next tm = Timer - tm Debug.Print tm End Sub |
実行結果は平均すると17.6秒でした。
これが遅いかどうかを比較するために、単に”a”を30万回代入する処理をやってみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
Sub stringSetTest() Dim s As String Dim i Dim tm tm = Timer For i = 0 To 300000 s = "a" Next tm = Timer - tm Debug.Print tm End Sub |
平均0.04秒です。
この結果からも、文字列の連結には時間が掛かることが分かります。
Join関数を使って連結する(速い方法)
先の処理ではループの度に&で連結していましたが、次のコードはループ処理では連結せずに1次元配列に格納して、ループを抜けたらJoin関数で連結する方法です。
なお、Join関数については「VBAで配列の全要素を連結して文字列にする(Join)」をご参照ください。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
Sub stringJoinTest() Dim ar() As String Dim i Dim s ReDim ar(300000) Debug.Print Timer For i = 0 To 300000 ar(i) = "a" Next Debug.Print Timer s = Join(ar, "") Debug.Print Timer End Sub |
平均0.04秒です。
先の代入だけの速度とほとんど同じです。
ループ中は配列に代入し、ループ後にJoin関数を1度実行しただけのため、ほとんど同じなのも理解できます。
文字列の連結よりも圧倒的に速いことが分かります。
ただ、Redimで最初に30万の要素を確保しているのも速い理由ではないか、というのもあるので、次にループ中でRedim Preserveで配列の領域を拡張するコードにしてみます。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 |
Sub stringJoinTest2() Dim ar() As String Dim i Dim s ReDim ar(0) Debug.Print Timer For i = 0 To 300000 ReDim Preserve ar(i) ar(i) = "a" Next Debug.Print Timer s = Join(ar, "") Debug.Print Timer End Sub |
平均0.2秒です。
配列の拡張に0.16秒ほど掛かっていると思われますが、それでも文字列の連結よりは速いことが分かります。
ただ、先のコードのようにRedimで事前に要素数を確保しておく方が速いので、可能であればそのようなコーディングをおすすめします。
まとめ
回数が多いループで文字列の連結を行う場合は配列に格納して、ループ後にJoin関数で連結するようにしましょう。
コードが複雑になることもありませんので、普段使いしてもいいと思います。