[C#]文字列の結合

C#で文字列の結合は「+」演算子で行うことが出来ますが、下記のコードのようにループで文字列を継ぎ足す場合には処理速度が極端に落ちます。

string str = "insert into [T_Test](ID, Value) values";
for (int i = 1; i < 10000; i++)
{
    str += string.Format("('{0}','{1}'),", iID[i], dDat[i]);
}

内部の処理を考えれば処理速度が落ちるのは当然です。文字列を結合する度にメモリの領域を確保し直している(文字列型を文字型の配列として)ところ、このメモリの領域を確保するのに時間がかかるためです。実際にデータベースにデータを格納するSQLを上記のようなコードで作ったところ、予想以上に時間がかかってしまいました。

複数ある文字列結合の方法(マイクロソフト社サイトへLink)などを参考にすると、①+演算子、②StringBuilder.Append、③string.Join、④string.Contactの4つの方法が主要なようです。
ここでは、上記の主要4手法による文字列結合を10,000回繰り返す処理の所要時間を比較してみたいと思います。また、この処理を1000回実施して、それぞれの試行回数による処理時間の違いについても見てみたいと思います。
具体定期なサンプルコードは下記の通りです。

using System.IO;

const int iN = 1000;
const int iRoop = 10000;
const int iMethod = 0; //ここの値で使用する処理方法を変える

static void Main(string[] args)
{
    string sOutF = Path.Combine(Directory.GetCurrentDirectory(), string.Format("{0}.txt", iMethod));
    using (StreamWriter sw = new StreamWriter(sOutF, false, Encoding.ASCII))
    {
        for (int i = 1; i <= iN; i++)
        {
            DateTime dtStart = DateTime.Now;
            string sCmd = "insert into [T_Test](ID, Value) values";
            StringBuilder sb = new StringBuilder(sCmd);
            string[] ss = new string[iRoop+1]; ss[0] = sCmd;
            List<string> lsS = new List<string>(); lsS.Add(sCmd);
            for (int j = 1; j <= iRoop; j++)
            {
                string sAppend = string.Format("('{0}','{1}'),", j, j / i);
                if (iMethod == 0)
                {
                    // +=演算子
                    sCmd += sAppend;
                }
                else if (iMethod == 1)
                {
                    sb.Append(sAppend);
                }
                else if (iMethod == 2)
                {
                    ss[j] = sAppend;
                    //lsS.Add(sAppend);
                }
                else
                {
                    sCmd = string.Concat(sCmd, sAppend);
                }
            }

            if (iMethod == 1)
            {
                sCmd = sb.ToString();
            }
            else if (iMethod == 2)
            {
                sCmd = string.Join("", ss);
            }
            sw.WriteLine("{0}\t{1}", i, (DateTime.Now - dtStart).Milliseconds);
        }
    }
}

まず、+演算子(+=)による処理時間は、平均で113.1msでした。また、処理を繰り返すうちに若干の時間短縮が見られました。

+演算子による結合での所要時間

次に、StringBuilder.Appendによる所要時間は、平均で1.9msと+演算子の1/60という結果になりました。文字列結合を繰り返す(回数が多い)場合には、この手法の方が圧倒的に優位なようです。

StringBuilder.Appendによる結合での所要時間

string.Joinは、引数にstring[]とした場合は平均で2.1ms、Listを引数とした場合は平均で2.2msとなりました。両者の差はほとんどありませんでした。また、StringBuilder.Appendの所要時間との差も小さいので、あらかじめ配列に結合したい文字列が入っている場合は、こちらの方法が良さそうです。

string.Join(string[])による結合での所要時間


string.Join(List)による結合の所要時間

string.Contactによる結合の所要時間は平均で115.5msとなりました。このコードのような使い方では文字列を結合するたびにメモリの確保が発生するために、結果として+演算子の場合と同じような結果となっています。

string.Contactによる結合の所要時間

上記の結果を踏まえると、文字列結合を繰り返す場合(特に長い文字列)が出来る場合は、StringBuilder.Appendを使うか、結合したい文字列を配列やリストに準備したうえで最後にstring.Joinで結合するのが良さそうです。

コメントの入力は終了しました。