Its always a good thing to know how to get the very best out of .NET. One area that is always overlooked is string concatenation. You have several ways to do this including:
- string x = "HELLO" + " " + "THERE";
- string x = "HELLO "; x = x + "THERE";
- string x = "HELLO "; x += "THERE";
- string x = String.Concat("HELLO"," ","THERE");
Although these are syntactically correct, they eat memory up faster than a buffet lunch at a fat fighters convention!
This is the process that is carried out by the application:
string text = text + otherText; - Allocates temporary memory large enough to hold the result.
- Copies text to the start of the temporary area.
- Copies otherText to the end of the temporary area.
- De-allocates the old copy of text.
- Allocates memory for text large enough to hold the result.
- Copies the temporary data to text.
The best and fastest way by far is the StringBuilder class. The class allows the addition of data via the .Append() and .AppendFormat() methods. Below I have placed some statistics of the concatentation process on my machine. This test appended the string ABCDEFGHIJKLMNOPQRSTUVWXYZ to itself the specified number of times.
| Method | Iterations | Time taken (s) |
| += | 1000 | 0.046875 |
| String.Concat() | 1000 | 0.046875 |
| StringBuilder.Append() | 1000 | 0.0 |
| |
| += | 2000 | 0.3125 |
| String.Concat() | 2000 | 0.375 |
| StringBuilder.Append() | 2000 | 0.0 |
| |
| += | 4000 | 2.34375 |
| String.Concat() | 4000 | 2.359375 |
| StringBuilder.Append() | 4000 | 0.0 |
| |
| += | 8000 | 8.984375
|
| String.Concat() | 8000 | 9 |
| StringBuilder.Append() | 8000 | 0.015625
|
| |
| += | 16000 | 31.546875 |
| String.Concat() | 16000 | 31.015625 |
| StringBuilder.Append() | 16000 | 0.03125 |
As you can see the values are going up exponentially for the previous 2 methods, whereas the .Append methods are still extremely fast!
Give it a try. Here is the code I used.
using System;
using System.Collections.Generic;
using System.Text;
namespace StringAppending
{
class Program
{
const string TEXT = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
static void Main(string[] args)
{
System.Threading.Thread.CurrentThread.Priority = System.Threading.ThreadPriority.Highest;
int[] iterations = new int[] { 1000, 2000, 4000, 8000, 16000 };
Console.WriteLine("Testing String comparisons.");
string[] headings = new string[] { "Method".PadRight(20), "Iterations".PadRight(20), "Time Taken (s)".PadRight(20) };
string[] tests = new string[] { "+=".PadRight(20), "String.Concat".PadRight(20), "StrBuilder.Append".PadRight(20) };
for (int counter = 0; counter < iterations.Length; counter++)
{
string iterationString = iterations[counter].ToString().PadRight(20);
double test1Result = StringPlus(iterations[counter]).TotalSeconds;
double test2Result = StringConcat(iterations[counter]).TotalSeconds;
double test3Result = StringAppend(iterations[counter]).TotalSeconds;
Console.WriteLine("Iterations: {0}\n====================", iterationString);
Console.WriteLine("{0}{1}{2}", headings[0], headings[1], headings[2]);
Console.WriteLine("{0}{1}{2}", tests[0], iterationString, test1Result);
Console.WriteLine("{0}{1}{2}", tests[1], iterationString, test2Result);
Console.WriteLine("{0}{1}{2}", tests[2], iterationString, test3Result);
Console.WriteLine("");
}
Console.Read();
}
private static TimeSpan StringPlus(int iterations)
{
DateTime start = DateTime.Now;
string textToAppend = TEXT;
string content = string.Empty;
int totalIterations = iterations * textToAppend.Length;
for (int counter = 0; counter < iterations; counter++)
{
content += textToAppend;
if (counter % 100 == 0)
Console.Write("{0:P} complete. (1/3) \r", ((float)content.Length / totalIterations));
}
return DateTime.Now.Subtract(start);
}
private static TimeSpan StringConcat(int iterations)
{
DateTime start = DateTime.Now;
string textToAppend = TEXT;
string content = string.Empty;
int totalIterations = iterations * textToAppend.Length;
for (int counter = 0; counter < iterations; counter++)
{
content = String.Concat(content, textToAppend);
if (counter % 100 == 0)
Console.Write("{0:P} complete. (2/3)\r", ((float)content.Length / totalIterations));
}
return DateTime.Now.Subtract(start);
}
private static TimeSpan StringAppend(int iterations)
{
DateTime start = DateTime.Now;
string textToAppend = "";
int totalIterations = iterations * textToAppend.Length;
StringBuilder content = new StringBuilder();
for (int counter = 0; counter < iterations; counter++)
{
content.Append(textToAppend);
if (counter % 100 == 0)
Console.Write("{0:P} complete. (3/3)\r", ((float)content.Length / totalIterations));
}
return DateTime.Now.Subtract(start);
}
}
}