Java Compiler replacing StringBuilder with + concatenation
Asked Answered
S

4

4

Here's some simple Java code:

String s = new StringBuilder().append("a").append("b").append("c").toString();

I compile it with JRE 1.6, and I observe the following in the decompiled class file:

String s = "a" + "b" + "c";

I had the following questions:

  1. Why does the compiler choose '+' over StringBuilder?
  2. Do we have any official Java specification that justifies this behavior?
  3. Does it really make sense to use StringBuilder in such cases, where we know compiler is going to change it anyway?
Scourings answered 25/9, 2014 at 21:43 Comment(3)
Are you sure you've decompiled the correct version of your class?Railhead
What you're seeing is the decompiler trying to be smart, not how the compiled code actually looks.Xanthin
StringBuilder for constant strings does not make sense. If I write String s = "a" + "b" + "c"; compiler produces line that using javap -c is shown like this: 0: ldc #2 // String abc. So it is smart and constant strings are "glued" into one. This means we can use + for code formatting purposes "for free" which is quite important.Acth
P
18

It's the other way round. + for String is implemented using StringBuilder (or StringBuffer) behind the scenes (see http://docs.oracle.com/javase/7/docs/api/java/lang/String.html or http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.18.1).

Thus, once they're compiled, your two code snippets are indistinguishable. The decompiler has to make a guess at the original form.

Pruitt answered 25/9, 2014 at 21:47 Comment(0)
R
2
  1. The decompiler must have assumed that the bytecode instructions to call append over and over resulted from source code that used the + operator.

  2. Section 15.18.1 of the JLS specifies that a compiler can use a StringBuffer or similar means to implement the + operator between Strings:

    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.

    The StringBuilder class is a "similar technique".

  3. In this case, you can use either technique to concatenate your strings. If you have lots of complicated operations yielding strings that need to be concatenated, then you would be better off using a StringBuilder and appending them yourself.

Rheostat answered 25/9, 2014 at 21:48 Comment(0)
B
1

I am not sure about JDK 1.6 (javac is part of JDK) but when I compile it with JDK 1.7, I get an appropriate disassembly.

The compilers are smart. In JDK, I think if you have s = "a" + "b" + "c" it will perhaps do it this way (use StringBuilder instead), but not the other way. More concretely, if all strings are compile-time constants (string literals) it will do even better -- calculate the string literal and replace the concatenation with that, so there will be less overhead at runtime.

Bumf answered 25/9, 2014 at 21:53 Comment(0)
G
0

Just For Reference

Your questions have already been answered by Oliver Charlesworth but I think you are checking the wrong place to start this question.

You should check the ByteCode using javap -v YouClass.class instead of using the IDE to check the decompiled code which can be quite confusing.

The original java code

public class LockElimination {
    private static String concat(String s1, String s2, String s3) {
        return s1 + s2 + s3;
    }
}

The ByteCode generated by javap -v LockElimination.class

Constant pool:
   #1 = Methodref          #7.#16         // java/lang/Object."<init>":()V
   #2 = Class              #17            // java/lang/StringBuilder
   #3 = Methodref          #2.#16         // java/lang/StringBuilder."<init>":()V
   #4 = Methodref          #2.#18         // java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   #5 = Methodref          #2.#19         // java/lang/StringBuilder.toString:()Ljava/lang/String;
Gestation answered 22/3, 2019 at 11:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.