According to the following unit test methods, StringBuilder is far slower than String.Replace, how come every one saying StringBuilder is faster? Am I missing something?
[TestMethod]
public void StringReplace()
{
DateTime date = DateTime.Now;
string template = File.ReadAllText("file.txt");
for (int i = 0; i < 100000; i++)
{
template = template.Replace("cat", "book" );
template = template.Replace("book", "cat");
}
Assert.Fail((DateTime.Now - date).Milliseconds.ToString());
}
[TestMethod]
public void StringBuilder()
{
DateTime date = DateTime.Now;
StringBuilder template = new StringBuilder(File.ReadAllText("file.txt"));
for (int i = 0; i < 100000; i++)
{
template.Replace("cat", "book");
template.Replace("book", "cat");
}
Assert.Fail((DateTime.Now - date).Milliseconds.ToString());
}
Here is the result:
StringReplace - 335ms
StringBuilder - 799ms
According to several tests (links to more tests at the bottom) as well as a quick and sloppy test of my own, String.Replace performs better than StringBuilder.Replace. You do not seem to be missing anything.
For completeness sake, here's my testing code:
int big = 500;
String s;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; ++i)
{
sb.Append("cat mouse");
}
s = sb.ToString();
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < big; ++i)
{
s = s.Replace("cat", "moo");
s = s.Replace("moo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
for (int i = 0; i < big; ++i)
{
sb.Replace("cat", "moo");
sb.Replace("moo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
for (int i = 0; i < big; ++i)
{
s = s.Replace("cat", "mooo");
s = s.Replace("mooo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
for (int i = 0; i < big; ++i)
{
sb.Replace("cat", "mooo");
sb.Replace("mooo", "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds);
The output, on my machine, is:
9
11
7
1977
[EDIT]
I missed one very important case. That is the case where every time the string is replaced with something else. This could matter because of the way C# handles strings. What follows is the code that tests the missing case, and the results on my system.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Text;
class Program
{
static void Main()
{
var repl = GenerateRandomStrings(4, 500);
String s;
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 100; ++i)
{
sb.Append("cat mouse");
}
s = sb.ToString();
Stopwatch sw = new Stopwatch();
sw.Start();
foreach (string str in repl)
{
s = s.Replace("cat", str);
s = s.Replace(str, "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds); sw.Reset(); sw.Start();
foreach (string str in repl)
{
sb.Replace("cat", str);
sb.Replace(str, "cat");
}
sw.Stop(); Trace.WriteLine(sw.ElapsedMilliseconds);
}
static HashSet<string> GenerateRandomStrings(int length, int amount)
{
HashSet<string> strings = new HashSet<string>();
while (strings.Count < amount)
strings.Add(RandomString(length));
return strings;
}
static Random rnd = new Random();
static string RandomString(int length)
{
StringBuilder b = new StringBuilder();
for (int i = 0; i < length; ++i)
b.Append(Convert.ToChar(rnd.Next(97, 122)));
return b.ToString();
}
}
Output:
8
1933
However, as we start to increase the length of the random strings, the StringBuilder solution comes closer and closer to the String solution. For random strings with a length of 1000 characters, my results are
138
328
Using this new knowledge on the old tests, I get similar results when increasing the length of the string to replace with. When replacing with a string that is a thousand 'a' characters instead of "mooo", my results for the original answer become:
8
11
160
326
Although the results do become closer, it still seems that for any real world use, String.Replace beats StringBuilder.Replace.