概要
C#には文字列を結合する方法がいくつかあります。
今回はそのうち5つを試してみたので、その結果を記載します。
検証した方法
・+演算子
・StringBuilder
・文字列補間→$"Name:{name}"など($ - 文字列補間 - C# リファレンス | Microsoft Docs)
・string.Format
・String.Concat
検証の設定
・それぞれの方法を100,000,000回(1億回)繰り返し、処理時間を計測
・System.Diagnostics.Stopwatchクラスを使用して繰り返し文の前後で
スタート・ストップをして時間を出力
検証
以下の関数を実行して計測
private static void StringMixTestA()
{
Console.WriteLine("--- 計測開始A ---");
string strA = "TestStringA";
string strB = "TestStringB";
var stopWatch = new System.Diagnostics.Stopwatch();
// 通常の"a" + "b";
stopWatch.Start();
for (int i = 0; i < testCount; i++)
{
var result = "Test A:" + strA + " B:"+ strB;
}
stopWatch.Stop();
Console.WriteLine("Plus:\t\t" + stopWatch.Elapsed.ToString());
// StringBuilderのAppend"a" + "b";
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Test A:");
stringBuilder.Append(strA);
stringBuilder.Append(" B:");
stringBuilder.Append(strB);
}
stopWatch.Stop();
Console.WriteLine("StringBuilder:\t" + stopWatch.Elapsed.ToString());
// 文字列補間
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = $"Test A:{strA} Test B:{strB}";
}
stopWatch.Stop();
Console.WriteLine("$:\t\t" + stopWatch.Elapsed.ToString());
// string.Format()
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = string.Format("Test A:{0} Test B:{1}", strA, strB);
}
stopWatch.Stop();
Console.WriteLine("string.Format:\t" + stopWatch.Elapsed.ToString());
// String.Concat
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = String.Concat("Test A:", strA, " Test B:", strB);
}
stopWatch.Stop();
Console.WriteLine("String.Concat:\t" + stopWatch.Elapsed.ToString());
Console.WriteLine("--- 計測終了A ---");
}
検証結果
方法 | 計測時間(時:分:秒) |
---|---|
Plus | 00:00:08.8524457 |
StringBuilder | 00:00:16.8160575 |
$ | 00:00:08.7606260 |
string.Format | 00:00:25.5426815 |
String.Concat | 00:00:08.6294175 |
というわけでString.Concatが一番早いようですが、
調べてみるとStringBuilderが早いという記事もあったので
別パターンも書いて計測してみました。
検証B
以下の関数を実行して計測。
先に変数を定義し、+=で結合する形に変更。
※StringBuilderは初めの検証と同じ
private static void StringMixTestB()
{
Console.WriteLine("--- 計測開始B ---");
string strA = "TestStringA";
string strB = "TestStringB";
var stopWatch = new System.Diagnostics.Stopwatch();
// 通常の"a" + "b";
stopWatch.Start();
for (int i = 0; i < testCount; i++)
{
var result = "";
result += "Test A:" + strA + " B:" + strB;
}
stopWatch.Stop();
Console.WriteLine("Plus:\t\t" + stopWatch.Elapsed.ToString());
// StringBuilderのAppend"a" + "b";
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Test A:");
stringBuilder.Append(strA);
stringBuilder.Append(" B:");
stringBuilder.Append(strB);
}
stopWatch.Stop();
Console.WriteLine("StringBuilder:\t" + stopWatch.Elapsed.ToString());
// 文字列補間
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = "";
result += $"Test A:{strA} Test B:{strB}";
}
stopWatch.Stop();
Console.WriteLine("$:\t\t" + stopWatch.Elapsed.ToString());
// string.Format()
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = "";
result += string.Format("Test A:{0} Test B:{1}", strA, strB);
}
stopWatch.Stop();
Console.WriteLine("string.Format:\t" + stopWatch.Elapsed.ToString());
// String.Concat
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = "";
result += String.Concat("Test A:", strA, " Test B:", strB);
}
stopWatch.Stop();
Console.WriteLine("String.Concat:\t" + stopWatch.Elapsed.ToString());
Console.WriteLine("--- 計測終了B ---");
}
検証結果B
方法 | 計測時間(時:分:秒) |
---|---|
Plus | 00:00:17.1915427 |
StringBuilder | 00:00:16.7240321 |
$ | 00:00:17.1817604 |
string.Format | 00:00:26.1338767 |
String.Concat | 00:00:17.0150580 |
この結果ではStringBuilderが一番早いですね。
すでに文字列があり、そこに足していくような場合はStringBuilderが良さそうです。
検証C
以下の関数を実行して計測。
先に変数を定義し、そのまま代入する形に変更。
※StringBuilderは初めの検証と同じ
private static void StringMixTestC()
{
Console.WriteLine("--- 計測開始C ---");
string strA = "TestStringA";
string strB = "TestStringB";
var stopWatch = new System.Diagnostics.Stopwatch();
// 通常の"a" + "b";
stopWatch.Start();
for (int i = 0; i < testCount; i++)
{
var result = "";
result = "Test A:" + strA + " B:" + strB;
}
stopWatch.Stop();
Console.WriteLine("Plus:\t\t" + stopWatch.Elapsed.ToString());
// StringBuilderのAppend"a" + "b";
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Test A:");
stringBuilder.Append(strA);
stringBuilder.Append(" B:");
stringBuilder.Append(strB);
}
stopWatch.Stop();
Console.WriteLine("StringBuilder:\t" + stopWatch.Elapsed.ToString());
// 文字列補間
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = "";
result = $"Test A:{strA} Test B:{strB}";
}
stopWatch.Stop();
Console.WriteLine("$:\t\t" + stopWatch.Elapsed.ToString());
// string.Format()
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = "";
result = string.Format("Test A:{0} Test B:{1}", strA, strB);
}
stopWatch.Stop();
Console.WriteLine("string.Format:\t" + stopWatch.Elapsed.ToString());
// String.Concat
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
var result = "";
result = String.Concat("Test A:", strA, " Test B:", strB);
}
stopWatch.Stop();
Console.WriteLine("String.Concat:\t" + stopWatch.Elapsed.ToString());
Console.WriteLine("--- 計測終了C ---");
}
検証結果C
方法 | 計測時間(時:分:秒) |
---|---|
Plus | 00:00:08.2999059 |
StringBuilder | 00:00:16.2361791 |
$ | 00:00:08.5158497 |
string.Format | 00:00:25.3592823 |
String.Concat | 00:00:08.5106941 |
この場合は+演算子が一番早かったですが、変数展開やString.Concatとは誤差程度ですね。
検証D
以下の関数を実行して計測。
StringBuilderが次々追加するのに対し、
Plusがいっぺんに合成しているのが不公平だと思ったので
+で一つの要素ごとに追加するようにしてみた。
private static void StringMixTestD()
{
Console.WriteLine("--- 計測開始D ---");
string strA = "TestStringA";
string strB = "TestStringB";
var stopWatch = new System.Diagnostics.Stopwatch();
// 通常の"a" + "b";
stopWatch.Start();
for (int i = 0; i < testCount; i++)
{
var result = "";
result += "Test A:";
result += strA;
result += " B:";
result += strB;
}
stopWatch.Stop();
Console.WriteLine("Plus:\t\t" + stopWatch.Elapsed.ToString());
// StringBuilderのAppend"a" + "b";
stopWatch.Restart();
for (int i = 0; i < testCount; i++)
{
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("Test A:");
stringBuilder.Append(strA);
stringBuilder.Append(" B:");
stringBuilder.Append(strB);
}
stopWatch.Stop();
Console.WriteLine("StringBuilder:\t" + stopWatch.Elapsed.ToString());
Console.WriteLine("--- 計測終了D ---");
}
検証結果D
方法 | 計測時間(時:分:秒) |
---|---|
Plus | 00:00:16.1928413 |
StringBuilder | 00:00:15.2582835 |
何度か合成を行う場合はStringBuilderの方が早いということがわかりました。
まとめ
例えば"100.0kg"のような数値+単位を表すような決まった文字列の場合は
速度の面で言えば+演算子、変数展開、String.Concatがよいでしょう。
より可読性が高いのは変数展開だと私は思っているので
文字列補間をおすすめしたいです。
何度も同じ変数に文字列を足していくような処理の場合は
StringBuilderがよさそうです。