AlwaysUseStringBufferMisconception < Javapedia < TWiki
|
TWiki . Javapedia . AlwaysUseStringBufferMisconception
|
Origin of the Misconception
Often, one of the things an intermediate Java developer discovers is how String concatenation using the overloaded + operator is implemented. We learn that at runtime, a new Object must be created when the two Strings are put together. This can create a serious performance issue when done inside of a loop as follows:
String s = "";
for (int i = 0; i < 1000; i++)
{
s += i;
}
What actually happens at runtime is that in every iteration of the loop, a new String is created and assigned to s. The solution is to use a StringBuffer to avoid creating a thousand new Objects.
This is all true. You may be told, however, that you should never use the overloaded + operator for Strings and that a StringBuffer should always be used to concatentate Strings. This is not true.
Concatenating Literal Strings
Let's look at one form of String concatentation used in Java code: concatenating two or more literal Strings and it's StringBuffer analog.
String s = "This is the first line\n"
+"This is the second line";
System.out.println(s);
StringBuffer b = new StringBuffer("This is the first line\n");
b.append("This is the second line");
System.out.println(b);
When compiled and run these both produce the same output. Using javap with the -c option, lets examine the bytecodes produced by the Sun 1.4.1_02-b6 compiler.
Here are the bytecodes for the concatenation version:
0 ldc #2 <String "This is the first line
This is the second line">
2 astore_1
3 getstatic #3 <Field java.io.PrintStream out>
6 aload_1
7 invokevirtual #4 <Method void println(java.lang.String)>
And these are the bytecodes for the StringBuffer version:
10 new #5 <Class java.lang.StringBuffer>
13 dup
14 ldc #6 <String "This is the first line
">
16 invokespecial #7 <Method java.lang.StringBuffer(java.lang.String)>
19 astore_2
20 aload_2
21 ldc #8 <String "This is the second line">
23 invokevirtual #9 <Method java.lang.StringBuffer append(java.lang.String)>
26 pop
27 getstatic #3 <Field java.io.PrintStream out>
30 aload_2
31 invokevirtual #10 <Method void println(java.lang.Object)>
Without getting into what all these things mean, we can see one thing easily. The concatenation version is doing a lot less work. The reason is that the compiler will inline the concatenation of literal Strings. In other words, it 'realizes' that those Strings are not going to change, so it concatenates them at compile time. In the StringBuffer version, on the other hand, we have forced runtime concatenation and actually degraded performance. The more literal Strings we concatenate, the worse the StringBuffer solution performs relative to using the overloaded + operator.
Concatenating Non-Literal Strings Outside of a Loop
So what about non-literal concatenations? Let's look at another example.
String s1 = "this is the first line\n";
String s2 = "this is the second line";
s1 += s2;
and
String s1 = "this is the first line\n";
String s2 = "this is the second line";
StringBuffer b = new StringBuffer();
b.append(s1);
s1 = b.append(s2).toString();
And using javap we get:
0 ldc #2 <String "this is the first line
">
2 astore_1
3 ldc #3 <String "this is the second line">
5 astore_2
6 new #4 <Class java.lang.StringBuffer>
9 dup
10 invokespecial #5 <Method java.lang.StringBuffer()>
13 aload_1
14 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
17 aload_2
18 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
21 invokevirtual #7 <Method java.lang.String toString()>
24 astore_1
For the concatenation version, and:
0 ldc #2 <String "this is the first line
">
2 astore_1
3 ldc #3 <String "this is the second line">
5 astore_2
6 new #4 <Class java.lang.StringBuffer>
9 dup
10 invokespecial #5 <Method java.lang.StringBuffer()>
13 astore_3
14 aload_3
15 aload_1
16 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
19 pop
20 aload_3
21 aload_2
22 invokevirtual #6 <Method java.lang.StringBuffer append(java.lang.String)>
25 invokevirtual #7 <Method java.lang.String toString()>
28 astore_1
29 return
for the StringBuffer version.
If you look at these, you'll see that the only difference is that the StringBuffer version uses some extra instructions. Of course, as some people point out, there is no guarantee that the compiler will use StringBuffer for concatenation. This is true but in the case of concatenating two Strings both a non-StringBuffer concatenation and a StringBuffer concatenation require the creation of at least one more Object. In the case of the StringBuffer version, it's the StringBuffer itself. So you don't gain anything by using StringBuffer until you are concatenating at least three Strings if you gain anything at all. In reality, however, unless you are concatenating a large number of non-literal Strings the difference in performance is negligible. In practice, situations where are large number of non-literal Strings are concatenated outside of a loop are extremely rare. Using StringBuffer to concatenate a few normal sized non-literal Strings sacrifices readability for microscopic performance increases. 'Micro-optimizations' are generally considered bad practice and should be avoided. An exception to this rule is if you are concatenating to form a very large String (say thousands of characters) it can be beneficial to use a StringBuffer but only if you specify the initial size of the StringBuffer's underlying character array in the constructor. This saves the time required to 'resize' the char array (actually creating new arrays repeatedly.)
In summary, using StringBuffers when concatenating in a loop is good practice but otherwise, using the overloaded + operator with Strings is usually perfectly acceptable.
-- Main.dubwai - 18 Jun 2003
UPDATE-
With the addition of the StringBuilder class in 1.5, the non-literal concatenation operation is now even faster. The performance increase comes from StringBuilder having no synchronization overhead. If you used String concatenation in the past, you will automatically get this boost when you compile in 1.5. StringBuilder is also a better choice for your legitimate Builder/Buffer needs in any case where thread synchronization is not a concern.
-- Main.dubwai - 28 Mar 2006
----- Revision r9 - 15 Aug 2008 - 16:37:15 - Main.club_lowlow
|