James Michael Hare

...hare-brained ideas from the realm of software development...
posts - 136 , comments - 1096 , trackbacks - 0

My Links

News

Welcome to my blog! I'm a Sr. Software Development Engineer in Seattle, WA. I've been doing C++/C#/Java development for over 18 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.

Blogs I Read

MCC Logo MVP Logo

Follow BlkRabbitCoder on Twitter

Tag Cloud

Archives

Post Categories

C#/.NET Little Wonders: An Oft Overlooked String Constructor

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.

Background

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:

====================Acount 11123452===================

To create such a thing, we could easily make a heading using calculations such as this:

   1: public static string MakeHeading(string text)
   2: {
   3:     // yes we would validate all input first, but for brevity assume all valid
   4:  
   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;
   8:  
   9:     // make line!  Now all we need to do is code CreateLine()
  10:     return CreateLine(leftLength) + text + CreateLine(rightLength);
  11: }

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.

Possible Methods:

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)
   2: {
   3:     // pre-size string buffer to max to avoid resizing
   4:     var builder = new StringBuilder(size);
   5:     
   6:     for (int j = 0; j < size; j++)
   7:     {
   8:         builder.Append('-');
   9:     }
  10:     
  11:     return builder.ToString();
  12: }

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:

   1: public static string CreateLine(int size)
   2: {
   3:     // pre-size string buffer to max to avoid resizing
   4:     var builder = new StringBuilder(size);
   5:  
   6:     builder.Append('-', size);
   7:  
   8:     return builder.ToString();
   9: }

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:

   1: public static string CreateLine(int size)
   2: {
   3:     // obviously this is hard coded and we'd want to validate input
   4:     const string line = "----------------------------------------";
   5:  
   6:     return line.Substring(0, size);
   7: }

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).

The forgotten String constructor

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?

Comparing Performance

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
   2:  
   3: Using StringBuilder w/o loop over 1,000,000 iterations: 188 ms
   4:  
   5: Using Substring over 1,000,000 iterations: 83 ms
   6:  
   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.

Summary

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.

 

  Technorati Tags: ,,,,,

Print | posted on Thursday, September 1, 2011 6:45 PM | Filed Under [ My Blog C# Software .NET Little Wonders ]

Feedback

Gravatar

# re: C#/.NET Little Wonders: An Oft Overlooked String Constructor

Thanks. Didn't know about that.

Just to be an annoying reader, you could also use PadRight and PadLeft on a string to achieve the same effect. Not sure what the perf is like.
9/1/2011 9:42 PM | ross
Gravatar

# re: C#/.NET Little Wonders: An Oft Overlooked String Constructor

@Ross:

True, you could PadLeft and PadRight the '-' character to fill it to 80 columns, I'll compare the numbers and let you know.

Yeah, the main point was to illustrate the constructor and this is the first thing that came to mind. Obviously if you just wanted a line of 40 dashes this fits perfectly.
9/1/2011 10:24 PM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: An Oft Overlooked String Constructor

James,

As you mention...

"that may seem trivial, but can help improve your code"

For those of us who have been using .Net for awhile this may be true. However, there is always some of us just catching on!

Anyways, great series keep it up!
9/2/2011 12:01 AM | Dave
Gravatar

# re: C#/.NET Little Wonders: An Oft Overlooked String Constructor

There are also Append and Insert overloads on StringBuilder that do the same thing, and they support both char and string repetition.
9/2/2011 8:01 AM | Casey
Gravatar

# re: C#/.NET Little Wonders: An Oft Overlooked String Constructor

@Dave: Thanks!
@Casey: I updated using Append() that takes a repeated char, it's twice as fast as the original StringBuilder method, but still three times slower than the String() constructor.
9/2/2011 8:34 AM | James Michael Hare
Gravatar

# re: C#/.NET Little Wonders: An Oft Overlooked String Constructor

@Ross: I tested PadLeft() and PadRight() - btw you're getting ahead of me, pad methods are on my Little Wonders of String list to do!

As far as performance goes, to build the header line PadLeft/PadRight was slower. The constructor method with concatenation took 189 ms, whereas the padding method took 298 ms (over 1,000,000 iterations).
9/2/2011 9:02 AM | James Michael Hare
Post A Comment
Title:
Name:
Email:
Comment:
Verification:
 

Powered by: