Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why doesn't the Java compiler concatenate string literals if they appear after a variable?

Tags:

java

I have the following test code:

public class StringLiteralTest {
    static void testPrefix() {
        int i = 0;
        String prefixConcat = "a" + "b" + i;
    }

    static void testSuffix() {
        int i = 0;
        String suffixConcat = i + "c" + "d";
    }
}

The generated bytecode is:

Compiled from "StringLiteralTest.java"
public class StringLiteralTest {
  public StringLiteralTest();
    Code:
       0: aload_0
       1: invokespecial #8                  // Method java/lang/Object."<init>":()V
       4: return

  static void testPrefix();
    Code:
       0: iconst_0
       1: istore_0
       2: new           #15                 // class java/lang/StringBuilder
       5: dup
       6: ldc           #17                 // String ab
       8: invokespecial #19                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      11: iload_0
      12: invokevirtual #22                 // Method java/lang/StringBuilder.append:(I)Ljava/lang/StringBuilder;
      15: invokevirtual #26                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      18: astore_1
      19: return

  static void testSuffix();
    Code:
       0: iconst_0
       1: istore_0
       2: new           #15                 // class java/lang/StringBuilder
       5: dup
       6: iload_0
       7: invokestatic  #35                 // Method java/lang/String.valueOf:(I)Ljava/lang/String;
      10: invokespecial #19                 // Method java/lang/StringBuilder."<init>":(Ljava/lang/String;)V
      13: ldc           #41                 // String c
      15: invokevirtual #43                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      18: ldc           #46                 // String d
      20: invokevirtual #43                 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
      23: invokevirtual #26                 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
      26: astore_1
      27: return
}

In testPrefix(), the expression "a" + "b" is combined into the string literal "ab" by the Java compiler, but in testSuffix(), the expression "c" + "d" is not combined at compile-time.

Why can't the compiler combine the String literals in the second method?

The source file was compiled with the default Oracle JDK 8 javac.

like image 456
Siqi Lin Avatar asked Feb 25 '15 22:02

Siqi Lin


People also ask

Can you concatenate string literals?

String literalsThe code concatenates the smaller strings to create the long string literal. The parts are concatenated into a single string at compile time.

Can you concatenate strings in Java?

Concatenation is the process of combining two or more strings to form a new string by subsequently appending the next string to the end of the previous strings. In Java, two strings can be concatenated by using the + or += operator, or through the concat() method, defined in the java. lang. String class.

What happens when we concatenate a string in Java?

concat() Method util package concatenates one string to the end of another string. This method returns a string with the value of the string passed into the method, appended to the end of the string.

Why should you be careful about string concatenation (+) operator in loops in Java?

Concatenation of two Strings If you concatenate Stings in loops for each iteration a new intermediate object is created in the String constant pool. This is not recommended as it causes memory issues.


3 Answers

Because the string concatenation operator (+) is syntactically left associative:

For example, the expression:

a + b + c

is always regarded as meaning:

(a + b) + c

Admittedly, if b and c are strings, the expression is equivalent to a + (b + c). So a compiler could do what you suggest in that specific situation, but it is not mandated by the specification...

like image 148
assylias Avatar answered Sep 18 '22 08:09

assylias


String suffixConcat = i + "c" + "d";

is equivalent to

String suffixConcat = (i + "c") + "d";

You could argue that it is equivalent to this expression

String suffixConcat = i + ("c" + "d");

which will be optimized to

String suffixConcat = i + "cd";

I think, this it the reason why the byte code does not include that optimization is in Language specification (15.18.1. String Concatenation Operator +):

The String object is newly created (§12.5) unless the expression is a constant expression (§15.28).

In other words, (i + "c") has to be new string, and (i + "c") + "d" again has to be a new string.

However, a JIT compiler could internally apply the optimization as it does not change the observable semantics.

like image 22
Philipp Claßen Avatar answered Sep 20 '22 08:09

Philipp Claßen


This isn't a great answer.

The order of addition is defined and takes place from the left.

This:

public static void main (String[] args) 
{
    String x= "X" + 1 + 2;
    String y= 1 + 2 + "Y";

    System.out.println(x);
    System.out.println(y);
}

Has expected output

X12
3Y

So I'm guessing that the compiler spots that literal plus literal can be optimized to 'long literal' but doesn't recognise that reordering the operations would have the same outcome so the verbose code produced.

like image 30
Persixty Avatar answered Sep 21 '22 08:09

Persixty