StringBuilder vs. String.Concat

Did you know that the String.Concat is really optimized (fast)? I've always seen it as an equivialent of StringBuilder, but as it turns out, String.Concat can be the better choice in many areas.

As a recap of earlier blog posts, if you are going to concatenate two strings, you might as well use the add operator ( var result = "Hello " + "World";). However, if you want to build a larger string, you might want to consider one of the two constructs discussed in this post.

My recommendation is to use String.Concat for average-sized strings, and StringBuilder for larger strings that are built over time:

  // String.Concat
  logger.LogError(String.Concat(
    "Error doing this and that for ", customerId,
    ", doing action ", action,
    " with properties ", properties
  ));

  // StringBuilder
  StringBuilder buffer = new StringBuilder();
  string line = null;
  while((line = stream.ReadLine()) != null)
  {
    if(buffer.Length > 0)
      buffer.Append(Environment.NewLine);
    buffer.Append(line);
  }
  DoMagic(withString: buffer.ToString());

In a test application I made, I found that using String.Concat for average-sized operations like those above (read: when you know how many strings you are adding), is faster than using equivialent StringBuilder-code. This was quite intereresting. Below are the results, and below that, the actual code:

    Results: (25000000 iterations)
  • String.Concat: 1165ms
  • StringBuilder: 2662ms

Code:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;

namespace ConsoleApplication1
{
    class Program
    {
        static void Main(string[] args)
        {
            const string value = "Hello World! I like Ice Cream!";
            const int iterations = 5000;

            string[] payload = new string[iterations];
            Console.WriteLine("Creating strings ...");
            for (int i = 0; i < iterations; i++)
            {
                payload[i] = value;
            }

            Stopwatch concatWatch = new Stopwatch();
            Stopwatch builderWatch = new Stopwatch();

            for (int i = 0; i < iterations; i++)
            {
                Prepare();
                concatWatch.Start();
                String.Concat(payload);
                concatWatch.Stop();

                Prepare();
                builderWatch.Start();
                StringBuilder builder = new StringBuilder();
                for (int j = 0; j < iterations; j++)
                    builder.Append(payload[j]);
                builderWatch.Stop();
            }

            Console.WriteLine("Results: ({2} iterations)\n- String.Concat:\t{0}ms\n- StringBuilder:\t{1}ms", concatWatch.ElapsedMilliseconds, builderWatch.ElapsedMilliseconds, iterations * iterations);

            Console.ReadLine();
        }

        private static void Prepare() {
            GC.Collect();
            GC.WaitForPendingFinalizers();
        }
    }
}

Comments

Popular posts from this blog

Auto Mapper and Record Types - will they blend?

Unit testing your Azure functions - part 2: Queues and Blobs

Testing WCF services with user credentials and binary endpoints