Strange String pool behavior

JavaString

Java Problem Overview


I've got a question of some strange String pool behavior. I'm using == to compare equal Strings to find out whether they're in the pool or not.

public class StringPoolTest {
  public static void main(String[] args) {
    new StringPoolTest().run();
  }

  String giveLiteralString() {
    return "555";
  }

  void run() {
    String s1 = giveLiteralString() + "";
    System.out.println("555" == "555" + "");
    System.out.println(giveLiteralString() == giveLiteralString() + "");
  }
}

The output is:

true
false

which is a big surprise for me. Could anyone explain this please? I think something about this is taking place at the compilation time. But why does adding "" to a String makes any difference at all?

Java Solutions


Solution 1 - Java

"555" + ""

is a compile-time constant, whereas

giveLiteralString() + ""

isn't. Therefore the former compiles into just the string constant "555" and the latter compiles into the actual method invocation and concatenation, resulting in a fresh String instance.


Also see JLS §3.10.5 (String Literals):

> Strings computed by concatenation at run time are newly created and > therefore distinct.

Solution 2 - Java

After decompiling this line

System.out.println("555" == "555" + "");

I got this bytecode

    LINENUMBER 8 L0
    GETSTATIC java/lang/System.out : Ljava/io/PrintStream;
    ICONST_1
    INVOKEVIRTUAL java/io/PrintStream.println(Z)V
    ...

which is equivalent to

  System.out.println(true);

that means expression "555" == "555" + "" compiles to boolean true.

For giveLiteralString() == giveLiteralString() + "" javac built this bytecode

    LINENUMBER 8 L0
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String;
    NEW java/lang/StringBuilder
    DUP
    INVOKESTATIC Test1.giveLiteralString()Ljava/lang/String;
    INVOKESTATIC java/lang/String.valueOf(Ljava/lang/Object;)Ljava/lang/String;
    INVOKESPECIAL java/lang/StringBuilder.<init>(Ljava/lang/String;)V
    INVOKEVIRTUAL java/lang/StringBuilder.toString()Ljava/lang/String;
    IF_ACMPNE L1
    ...

which is equivalent to

if (giveLiteralString() == new StringBuilder(giveLiteralString()).append("").toString()) {
...

which will always produce false since here we're comparing 2 disctinct objects.

Solution 3 - Java

In the second case the compiler COULD have recognized that + "" is a no-op of sorts, since "" is a compile-time value known to be zero length. But the compiler is still required to check the result from giveLiteralString for null (since the null check would occur as a result of the + operation in the non-optimized case), so it's simplest to just not attempt the optimization.

As a result, the compiler generates code to perform the concatenation, and a new string is created.

Solution 4 - Java

Compile Time Concatenation String computed by constant expression are done at compile time and treated as constants or literals means the value of the string or expression is known or evaluated at compile time hence the compiler can check the same value in string pool and the return the same string object reference.

Runtime Concatenation String expressions whose values are known or can not be evaluated at compile but depends on the input or condition of run-time then the compiler will not know the value of the string and hence always land up using StringBuilder to append the string and always returns a new string . I guess this example will clarify it better.

public static void main(String[] args) {
    new StringPoolTest().run();
  }
  String giveLiteralString() {
    return "555";
  }

  void run() {
	System.out.println("555" + 9 == "555" + 9);  
    System.out.println("555"+Integer.valueOf(9) == "555" + Integer.valueOf(9)); 
    System.out.println(giveLiteralString() == giveLiteralString());
    // The result of runtime concatenation is a fresh string.
    System.out.println(giveLiteralString() == giveLiteralString() + "");
  }

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestioniozeeView Question on Stackoverflow
Solution 1 - JavaMarko TopolnikView Answer on Stackoverflow
Solution 2 - JavaEvgeniy DorofeevView Answer on Stackoverflow
Solution 3 - JavaHot LicksView Answer on Stackoverflow
Solution 4 - JavaHimansu MeherView Answer on Stackoverflow