Welcome to my blog! I'm a Sr. Software Development Engineer in the Seattle area, who has been performing C++/C#/Java development for over 20 years, but have definitely learned that there is always more to learn!
All thoughts and opinions expressed in my blog and my comments are my own and do not represent the thoughts of my employer.
Once again, in this series of posts I look at the parts of the .NET Framework that may seem trivial, but can help improve your code by making it easier to write and maintain. The index of all my past little wonders post can be found here.
I’m going to begin a series of Little Wonders in the BCL String class. Yes, we all work with strings in .NET daily, so perhaps you already know most of these. However, there are a lot of little fun things that the String class can do that often get overlooked and these next few posts will be dedicated to those String Little Wonders.
Today, in particular, we will discuss an often overlooked constructor that can be useful for constructing strings of a repeated character.
Sometimes in code you’ll see someone coding an application that writes to console or a log file, and for separation you may want a line of dashes to divide sections. Perhaps, you want a piece of text centered in a dashed line such as:
To create such a thing, we could easily make a heading using calculations such as this:
1: public static string MakeHeading(string text)
3: // yes we would validate all input first, but for brevity assume all valid
5: // left space is area / 2, right is same unless text is odd length, then same + 1
6: int leftLength = (80 - text.Length) / 2;
7: int rightLength = text.Length % 2 == 0 ? leftLength : leftLength + 1;
9: // make line! Now all we need to do is code CreateLine()
10: return CreateLine(leftLength) + text + CreateLine(rightLength);
But how do we make that line? Well, we know we want to repeat the character for leftLength times, but what’s the best way to do this.
Well, there are several ways we can accomplish making a string of repeated characters. Obviously the first is to just build in a for loop, and because we know that concatenating strings repeatedly is very slow, we’ll optimize a bit and use a StringBuilder. Further, because we know StringBuilder will resize when it grows beyond it’s buffer, we’ll pre-size the buffer using the StringBuilder constructor that takes a starting buffer size it to avoid this cost:
1: public static string CreateLine(int size)
3: // pre-size string buffer to max to avoid resizing
4: var builder = new StringBuilder(size);
6: for (int j = 0; j < size; j++)
11: return builder.ToString();
This is very straightforward and does what we need, but it has two direct allocations: one for the StringBuilder and one for the resulting String.
Update: a reader pointed out instead of looping the Append() I could call an overload of Append() that repeats a char. This should be more efficient than the method above, but still incurs the cost of allocating a StringBuilder(). This would look like:
6: builder.Append('-', size);
8: return builder.ToString();
But we can still do better; we could have a standard string of the maximum length we need, and then perform a substring on it to cut it down to the length we need:
3: // obviously this is hard coded and we'd want to validate input
4: const string line = "----------------------------------------";
6: return line.Substring(0, size);
The StringBuilder solutions seem the most straightforward, but the Substring() solution allocates less memory directly (the latter only allocates the String result from the substring, as opposed to the former which allocates a StringBuilder and the resulting String).
But there’s another way. There is a form of the String constructor which builds a string of a repeated character. To invoke it, you simply construct a String passing in a char to repeat and an int for the number of times to repeat it. It’s so compact we don’t even need to wrap this logic up in a method to reuse it! It’s already written for us in the constructor:
1: // creates a string of the specified char repeated the specified number of times
2: var str = new String('-', size);
So that’s pretty neat, we can quickly and easily create a String of a repeated character. But is it performant?
If we break out a de-compiler (such as Reflector) we can take a look at the definition of this String constructor:
1: public extern String(char c, int count);
Note that it’s extern, this means that it actually makes a call to a native method, which leads us to believe this has probably been optimized in native code to be extremely efficient.
So let’s compare these three methodologies over 1,000,000 iterations on strings of varying lengths. The results are:
1: Using StringBuilder w loop over 1,000,000 iterations: 360 ms
3: Using StringBuilder w/o loop over 1,000,000 iterations: 188 ms
5: Using Substring over 1,000,000 iterations: 83 ms
7: Using String() over 1,000,000 iterations: 62 ms
So as you can see, the String() constructor is actually the most efficient of all! In addition, it’s more flexible than the runner up (substring methodology) because this can be done on any length and any char and is still more efficient. Finally, the poor little for loop comes in last, most likely because it’s directly allocating two objects (String and StringBuilder) where the other methods only directly allocate a single String.
Update: using the form of Append() that repeats appending of a character, it’s half the time of the original StringBuilder test, but still three times higher than the String() constructor.
So in conclusion, if you need to create a string of a character repeated, use the String(char ch, int size) constructor, since it is the most straightforward and the most efficient. Stay tuned next week for more String Little Wonders.
Print | posted on Thursday, September 1, 2011 6:45 PM |
Filed Under [
Copyright © James Michael Hare