StringBuilder vs. String
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 theoperator + 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 calledStringBuilder, 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 usingString 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
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
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 usingStringBuilder 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).
Comments (7)