C# StringBuilder

We know strings are immutable and it means a string object cannot be changed once it is created. Thank about it, if we concatenate strings too much in a loop statement, we'll get a significant performance penalty because for each concatenation, a new string object will be created. Now the solution is to use StringBuilder instead of string to do the job. StringBuilder stands for a mutable string and it can be changed unlimited times without creating a new object.

When a StringBuilder object is created with its default constructor, a buffer is allocated in memory as well. The buffer will hold the characters which are added to the StringBuilder object. The default size of the buffer is 16 and can be doubled if its capacity is exceeded. The Length of the StringBuilder object is the amount of characters it holds. The Capacity of the object is the buffer size and the MaxCapacity is the maximum capacity of the buffer can be increased to. If the buffer size exceeds the MaxCapacity, an error message will be issued.

Example 01-83-01

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
using System;
using System.Text;

class Test
{
    static void output(StringBuilder sb)
    {
        Console.WriteLine("Length={0, -3}, Capacity={1, -3}, MaxCapacity={2, -10}", sb.Length, sb.Capacity, sb.MaxCapacity);
    }

    static void Main()
    {
        StringBuilder a = new StringBuilder();
        output(a);
        a.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(a);
        a.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(a);
        Console.WriteLine("int.MaxValue={0}", int.MaxValue);
        Console.WriteLine(a.ToString().Substring(0, 26).ToLower());

        StringBuilder b = new StringBuilder(10, 130);
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");
        output(b);
        //b.Append("ABCDEFGHIJKLMNOPQRSTUVWXYZ");   // Error Message
        //output(b);

        Console.Read();
    }
}

Output

Length=0  , Capacity=16 , MaxCapacity=2147483647
Length=26 , Capacity=32 , MaxCapacity=2147483647
Length=52 , Capacity=64 , MaxCapacity=2147483647
int.MaxValue=2147483647
abcdefghijklmnopqrstuvwxyz
Length=0  , Capacity=10 , MaxCapacity=130       
Length=26 , Capacity=26 , MaxCapacity=130       
Length=52 , Capacity=52 , MaxCapacity=130       
Length=78 , Capacity=104, MaxCapacity=130       
Length=104, Capacity=104, MaxCapacity=130       
Length=130, Capacity=208, MaxCapacity=130       
Length=156, Capacity=208, MaxCapacity=130       
Length=182, Capacity=208, MaxCapacity=130       
Length=208, Capacity=208, MaxCapacity=130       

Explanation

  • Line 2: StringBuilder is in System.Text namespace.
  • Line 6-9: Output the length, capacity and MaxCapacity of a StringBuilder object.
  • Line 13-18: The capacity doubled each time when the length of the object exceeds its capacity.
  • Line 19: The default MaxCapacity is the same as integer maximum value.
  • Line 20: ToString() can be used to convert a StringBuilder object to a string. The methods can be chained successively whenever they all return a string.
  • Line 22: Create a new StringBuilder object with capacity 10 and MaxCapacity 130.
  • Line 24-41: This is interesting as you see the result. The capacity can exceed MaxCapacity one time but an error message will be issued at the second time. I don't know if it is a bug from Microsoft.

We'll give the examples of the most used methods in programs. For all the methods in details, please check it here.

Example 01-83-02

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Text;

class Student
{
    string _name;

    public Student(string name){
        _name = name;
    }

    public override string ToString()
    {
        return _name;
    }
}

class Test
{
    static void Main()
    {
        StringBuilder a = new StringBuilder("de");
        double d = 10.0 / 3.0;
        a.Append(d);
        a.Append(new Student("Peter"));
        Console.WriteLine(a);

        a.Insert(0, "abc");
        a.AppendLine();
        a.AppendFormat("Today: {0:d}", DateTime.Now);
        Console.WriteLine(a);

        a.Clear();
        a.Append("123456");
        a.Insert(3, new char[] { 'X', 'Y', 'Z' });
        Console.WriteLine(a);

        a.Replace("XYZ", "ABC").Remove(1, 2);
        Console.WriteLine(a);

        a[4] = '0';
        Console.WriteLine(a);

        Console.Read();
    }
}

Output

de3.33333333333333Peter
abcde3.33333333333333Peter
Today: 11/14/2014
123XYZ456
1ABC456
1ABC056

Explanation

  • Line 4-16: Define a class student and a override method ToString().
  • Line 22-24: Create a StringBuilder object with initialized string "de". Then add a double type value to it.
  • Line 25: Append() method is also allowed to add an object. The object will be converted to a string and append to the end of the a. If the code in line 12-15 is not defined, the class name "Student" will be appended.
  • Line 28: Insert "abc" at the beginning of a.
  • Line 29: Append a new line to a.
  • Line 30: A formatted string is added as well.
  • Line 33: Remove all the characters in a.
  • Line 35: Insert a char array at the index of 3 of a.
  • Line 38: Replace "XYZ" with "ABC" in a and the result returns as a StringBuilder object. So it can be chained with Remove method. Remove(1, 2) will remove 2 characters starting from 1 location.
  • Line 41: The individual character is read-only in a string by [] operator but it can be changed in a StringBuilder object.

In the example below, we'll concatenate strings in a loop to test which is faster between string and StringBuilder.

Example 01-83-03

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
using System;
using System.Text;

class Test
{
    static void Main()
    {
        const int MAX = 100000;
        StringBuilder a = new StringBuilder();
        string b = String.Empty;

        DateTime t1 = DateTime.Now;
        for (int i = 0; i < MAX; i++)
        {
            a.Append("abc");
        }
        DateTime t2 = DateTime.Now;
        for (int i = 0; i < MAX; i++)
        {
            b += "abc";
        }
        DateTime t3 = DateTime.Now;

        Console.WriteLine("StringBuilder Spent:{0}", (t2 - t1).TotalMilliseconds);
        Console.WriteLine("string Spent:{0}", (t3 - t2).TotalMilliseconds);

        Console.Read();
    }
}

Output

StringBuilder Spent:0
string Spent:10019.5992

Explanation

  • Line 12, 17, 22: Record the current time.
  • Line 13-16, 18-21: 2 loops and each iteration "abc" will be added.
  • Line 24-25: Output the result. You can see the first loop using StringBuilder was finished instantly. But the second one using string took 10 seconds.