4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

【C#】文字列結合の負荷について検証

Posted at

概要

C#には文字列を結合する方法がいくつかあります。
今回はそのうち5つを試してみたので、その結果を記載します。

検証した方法

・+演算子
・StringBuilder
・文字列補間→$"Name:{name}"など($ - 文字列補間 - C# リファレンス | Microsoft Docs)
・string.Format
・String.Concat

検証の設定

・それぞれの方法を100,000,000回(1億回)繰り返し、処理時間を計測
・System.Diagnostics.Stopwatchクラスを使用して繰り返し文の前後で
 スタート・ストップをして時間を出力

検証

以下の関数を実行して計測

StringMixTestA
	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は初めの検証と同じ

StringMixTestB
	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は初めの検証と同じ

StringMixTestC
	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がいっぺんに合成しているのが不公平だと思ったので
+で一つの要素ごとに追加するようにしてみた。

StringMixTestC
	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がよさそうです。

4
3
2

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
4
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?