Operator Overloading Debate Finally finally (block)
Feb 16

A lot has been said, written and blogged on this subject, I know. I’ll try to cover it from the disassembly angle, though, after going over the main points of this comparison.

Why use StringBuilder?

A good question deserves a good answer. See, the String class is an immutable class - It will never really change internally. When you use the operator + it actually generates code that does it for you, and if you use the concat(String) it is clearly stated that a new string is created.

Worthy to note is that all of String methods do not change the string itself, but rather return a new String. More than once I’ve seen people calling replace and wondering why it does not work - They had forgot to take the returned value as the new value!

StringBuilder? I thought it was called StringBuffer!

The sharp eyed might notice that in J2SE 5.0, a new class was introduced called StringBuilder, while up until then there was a class called StringBuffer. Well, StringBuffer is still there, and I’ve discussed their differences earlier as an aside of a different post.

Basically, StringBuilder is more efficient as it is designed with no thread-safety in mind, thus no synchronisation is performed. The two classes share the same interface (even if not physically), so StringBuilder can be used as a drop-in to replace StringBuffer in single-thread environments.

Disassembly example

I have created two methods, one using String with the operator + and the other using StringBuilder. The two methods take an array of strings and create a single comma-separated list from them.


  public String with(String[] values) {
    StringBuilder sb = new StringBuilder(values[0]);

    for (int i = 1; i < values.length; i++) {
      sb.append(", ").append(values[i]);
    }

    return sb.toString();

  }

  public String without(String[] values) {
    String res = values[0];

    for (int i = 1; i < values.length; i++) {
      res += ", " + values[i];
    }

    return res;
  }

Now, this is the main loop’s disassembled code for the with method:


    19: aload_2
    20: ldc #4; //String ,
    22: invokevirtual #5; //Method StringBuilder.append(String):StringBuilder
    25: aload_1
    26: iload_3
    27: aaload
    28: invokevirtual #5; //Method StringBuilder.append(String):StringBuilder
    31: pop

You can see that only method calls were done here, once to append the comma and the other time to append the string. Now let’s take a look at without:


    12: new #2; //class StringBuilder
    15: dup
    16: invokespecial #7; //Method StringBuilder."<init>":()V
    19: aload_2
    20: invokevirtual #5; //Method StringBuilder.append(String):StringBuilder
    23: ldc #4; //String ,
    25: invokevirtual #5; //Method StringBuilder.append(String):StringBuilder
    28: aload_1
    29: iload_3
    30: aaload
    31: invokevirtual #5; //Method StringBuilder.append(String):StringBuilder
    34: invokevirtual #6; //Method StringBuilder.toString():String;
    37: astore_2

In laymen terms, in every iteration the loops creates a new StringBuilder (lines 12-16), appends to it the previous value of the string (lines 19-20), and only then does what the with code did: appends the comma and the next string (lines 23-31). Then, it sets the res string with the result of toString from the StringBuilder previously created.

I will repeat it, as this is important: This is done in every iteration. This means a lot of new StringBuilders, a lot of unneccessary appends of the previous string, and a lot of unnccessary toStrings. Can you imagine how much this costs in performence, both in memory and running times?

Can I always use StringBuilder?

The answer is varied. If you’re only building long strings, then using StringBuilder is recommended. For shorter strings, with fixed creations (such as res = a + " " + b + " : " + c;) it should be okay. For iterations over a group of values, I’d seriously recommend StringBuilder.

More to consider: There are a lot of things that you are unable to do with StringBuilder, such as use the internal string until you have toString‘ed it. You can’t match, split, search, or format a StringBuffer (even though regarding formatting I have talked about the more efficient MessageFormat before).

Share/Save/Bookmark

5 Responses to “StringBuilder vs. String”

  1. Sandy McArthur Says:

    The sort version is if you are joining Strings across many statements, use StringBuilder (or StringBuffer). If you can join Strings in one statement, save your time and use the + operator.

  2. Ahmed Hashim Says:

    I was going on to make a Test Script to measure the effect of loading 1 MB file using String concatenation while looping on the file rather than using StringBuffer appending. But I think you did a good job here.:)

    BTW how did you make the disassembled code?

    Thanks

  3. Avah Says:

    Thank you!

    To get the disassembled code, use the javap tool:

    javap -c qualified class name

  4. Joe7Pak Says:

    Just a thought -

    I’m looking in the docs ( for 6.0, don’t know if anything has changed since 5.0 ) and it says that the default ctor for StringBuilder has a buffer of 16 chars. Could it be that if you make the buffer bigger, say, 256 or 512, that you wouldn’t see new StringBuilders being built nearly as much?

  5. Avah Says:

    Joe7Pak: Yes, definitely! The only problem there is the need to pre-allocate 256 characters, but if you know that your target-string is going to be approximately that size, it’s worth it!

Leave a Reply

Chaotic Java is Digg proof thanks to caching by WP Super Cache!