Comparison of String and StringBuilder in C#

A string (namespace: System.String ) is a sequential collection of Unicode characters that represent text. A String object is immutable (read-only) and a sequential collection of System.Char objects that represent a string. The maximum size of a String object in memory is 2 GB (about 1 billion characters).

Although a string is a reference type, the equality operators (== and !=) are defined to compare the values of string objects, not references. However after boxing, the comparison happens on string instances.

  1. string a = "prakash";  
  2. string b = "p";  
  3. b += "rakash";  // Append to contents of 'b'  
  4. Console.WriteLine(a ==b);  //True  
  5. Console.WriteLine((object)a == (object)b); //False as after boxing the //comparison happens on string instance and both are differentstring instance   
On the other hand, a StringBuilder (System.Text.StringBuilder) represents a mutable string of characters. This class cannot be inherited.

The default capacity of this object is 16 characters, and its maximum capacity is more than 2 billion characters.

  1. StringBuilder sb = new StringBuilder();  
  2. sb.Append("Prakash");  
  3. sb.Append("Tripathi");  
  4. Console.WriteLine(sb.ToString());  
How it works

The compiler creates a new string object for each string concatenation to hold the new sequence of characters (in other words the existing string and the new data), and that new object is assigned back to the variable.

For example, if you are concatenating two strings then string concatenation would allocate a new string of length (str1.Length + str2.Length) and copy the first and second strings into place.

This results in many re-copying of strings, for example concatenation of N strings (var result = str1 + str2 + str3 + ... + strN;) will require N-1 allocations and N-1 copy operations. This can get very expensive for a large N.

On the other hand, a StringBuilder object maintains a buffer to accommodate the concatenation of new data. New data is appended to the buffer if room is available; otherwise, a new, larger buffer is allocated, data from the original buffer is copied to the new buffer, and the new data is appended to the new buffer.

For example, if you are concatenating 20 strings then StringBuilder simply appends one after another to its buffer and finally returns the result when requested.

When to use what

Do not use a string when you don't know the number of concatenations or if the number of concatenations are karge. For example it’s not good to use a string in the following example.

  1. //Inefficient code! Do not use!  
  2. string x = "";  
  3. for (int i = 0; i < 100000; i++)  
  4. {  
  5.     x += "!";  
  6. }  
Use the String class if you are concatenating a fixed number of String objects. In that case, the compiler may even combine individual concatenation operations into a single operation especially in the case of the concatenation of string literals as below.
  1. //Efficient code  
  2. stringx = "Hello" + " " + "Prakash";  
On the other hand, do not use StringBuilder for trivial concatenation since allocation and initialization of StringBuilder takes some time that may not be useful for a fewer number of concatenations.

For example, the following is the bad example of StringBuilder.

  1. //Inefficient code! Do not use!  
  2. StringBuilder sb = new StringBuilder();  
  3. sb.Append("Hello");  
  4. sb.Append("Prakash");  
  5. Console.WriteLine(sb.ToString());  
Use a StringBuilder object if you are concatenating an arbitrary number of strings; for example, if you're using a loop to concatenate a random number of strings of user input. Or in the case of a big loop as in the following.
  1. //Efficient code  
  2. StringBuilderbuilder = new StringBuilder();  
  3. for (int i = 0; i < 100000; i++)  
  4. {  
  5.     builder.Append("!");  
  6. }  
  7. x = builder.ToString();  
Performance Comparison

I have tested the performance comparison of String and StringBuilder in a large loop (100000 iterations) in my machine and the following are the findings.

  1. int dt1 = DateTime.Now.Millisecond;  
  2. Console.WriteLine();  
  3. string x = "";  
  4. for (int i = 0; i < 100000; i++)  
  5. {  
  6.     x += "!";  
  7. }  
  8. int dt2 = DateTime.Now.Millisecond;  
  9. Console.WriteLine("Time taken in string concatenation: {0} MS", dt2 - dt1);  
  10. int dt3 = DateTime.Now.Millisecond;  
  11. StringBuilderbuilder = new StringBuilder();  
  12. for (int i = 0; i < 100000; i++)  
  13. {  
  14.    builder.Append("!");  
  15. }  
  16. x = builder.ToString();  
  17. int dt4 = DateTime.Now.Millisecond;  
  18. Console.WriteLine("Time taken in StringBuilder concatenation: {0}MS", dt4 - dt3);  
StringBuilder in C# 

Please note that the difference in time in both can be even more when doubling the iterations. Also the results above could vary based on the machine configuration.

Some Tips 

StringBuilder can be even more efficient when limiting the size to its constructor to avoid doubling its buffer size when it runs out of room.

Also if you have an array of things to concatenate, consider calling "String.Concat" explicitly or "String.Join" if you need a delimiter.

References: MSDN and Stackoverflow