I was looking at the String
Javadoc when I noticed this bit about String concatenation:
The Java language provides special support for the string concatenation operator ( + ), and for conversion of other objects to strings.
String
concatenation is implemented through theStringBuilder
(orStringBuffer
)
From the Java 8 JLS 15.8.1, it is a choice given to the compiler (emphasis mine):
An implementation may choose to perform conversion and concatenation in one step to avoid creating and then discarding an intermediate String object. To increase the performance of repeated string concatenation, a Java compiler may use the StringBuffer class or a similar technique to reduce the number of intermediate String objects that are created by evaluation of an expression.
I made a small program to see what it compiles down to
public class Tester {
public static void main(String[] args) {
System.out.println("hello");
for (int i = 1; i < 5; i++) {
String s = "hi " + i;
System.out.println(s);
}
String t = "me";
for (int i = 1; i < 5; i++) {
t += i;
System.out.println(t);
}
System.out.println(t);
}
}
And the output when running javap -c Tester
shows that StringBuilder
is being used:
Compiled from "Tester.java"
public class Tester {
public Tester();
Code:
0: aload_0
1: invokespecial #1 // Method java/lang/Object."<init>":()V
4: return
public static void main(java.lang.String[]);
Code:
0: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
3: ldc #3 // String hello
5: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
8: iconst_1
9: istore_1
10: iload_1
11: iconst_5
12: if_icmpge 48
15: new #5 // class java/lang/StringBuilder
18: dup
19: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
22: ldc #7 // String hi
24: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
27: iload_1
28: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
31: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
34: astore_2
35: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
38: aload_2
39: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
42: iinc 1, 1
45: goto 10
48: ldc #11 // String me
50: astore_1
51: iconst_1
52: istore_2
53: iload_2
54: iconst_5
55: if_icmpge 90
58: new #5 // class java/lang/StringBuilder
61: dup
62: invokespecial #6 // Method java/lang/StringBuilder."<init>":()V
65: aload_1
66: invokevirtual #8 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
69: iload_2
70: invokevirtual #9 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
73: invokevirtual #10 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
76: astore_1
77: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
80: aload_1
81: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
84: iinc 2, 1
87: goto 53
90: getstatic #2 // Field java/lang/System.out:Ljava/io/PrintStream;
93: aload_1
94: invokevirtual #4 // Method java/io/PrintStream.println:(Ljava/lang/String;)V
97: return
}
I've looked at a handful of questions that tell the story that StringBuilder
is generally faster because of synchronization in StringBuffer
, and that is substituted for these string concatenations:
So, considering the things I've read that show that StringBuilder
is generally the better choice leads me to wonder a couple things:
StringBuffer
instead of StringBuilder
?AbstractStringBuilder
implementation?The specification’s wording you have cited origins from older specifications. The simple answer is, before Java 1.5 aka Java 5 there was no StringBuilder
. So older compilers didn’t have to choose between StringBuffer
and StringBuilder
and the specification at that time simply recommended using what is available.
With Java 5, StringBuilder
was introduced which doesn’t use synchronized
methods which is perfect for the use case of String
concatenation as that’s a pure local operation. So for compilers targeting 1.5
or higher, there is a choice (which is still covered by the specification’s words “or a similar technique”) and they will choose StringBuilder
as there is no reason for using StringBuffer
when targeting 1.5
or higher.
Starting with Java 9, there is a new technology using an invokedynamic
bytecode instruction with a bootstrap method from the StringConcatFactory
class, but it’s still covered by the specification’s words “or a similar technique”.
It depends on the compiler implementation (javac
is not the only compiler). However, there is never a good case for using StringBuffer
over StringBuilder
for these types of uses. It is always a single use narrowly scoped object where the synchronization of StringBuffer
provides no functional value.
So the short answer is that no compiler /should/ ever use StringBuffer
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With