Java String Equality Example
Sorry in advance for the main question, but can someone explain to me why:
String s = "lo";
String str7 = "Hel" + s;
String str8 = "He" + "llo";
System.out.println("str7 == str8 is " + (str7 == str8));
outputs false. I thought str7 and str8 both point to the same object in the string pool, because strings are immutable.
Am I wrong? Do str7 and str8 exist not in the pool but on the heap? Why?
Can you provide me with an example of some string manipulation where the result is actually the same immutable string from the string pool?
PS:
String str9 = "He" +"llo";
System.out.println("str8 == str9 is " + (str9 == str8));
outputs true
source to share
Your understanding is correct in the case of a pool, where all literals end up in the string pool.
And confusion arises when you do concatenation. There are two points to note.
1) If the String is resolved at compile time, yes, it syncs with the String pool and uses the same literals. For ex
String str7 = "Helllo";
String str8 = "He" + "llo";
Note that both are simple literals. Hence, there are no runtime conversions etc.
2) If String resolves at runtime, it resolves newline at runtime and differs with any other string, unless you use the .equals method to compare its contents.
String str7 = "Hel" + s;
String str8 = "He" + "llo";
System.out.println("str7 == str8 is " + (str7 == str8)); //false
In this case, concat strings with operator (+), the JVM returns new StringBuilder(string...).toString()
because it is a simple literal and the other is a variable.
Look at the Java Language Specification
If only one operand expression is of type String, then a string conversion (ยง5.1.11) is performed on the other operand to create a string at runtime.
Questions from comment:
does this mean that in case a string is created as the product of a concatenation of literals then the result is always the same string in the pool?
Yes it is. Remember, concatenation, which you mean by compile-time expression (as a constant expression), not Runtime.
And when we concatenate a string literal with a string object, then the resulting string is always a new string object created with a StringBuilder under the hood?
Yes, a new line was returned. The attached JVM connection confirms this.
source to share
String s = "lo";
String str7 = "Hel" + s;
String str8 = "He" + "llo";
String str9 = "He" + "llo";
The referenced s
object is a String object that represents the literal "lo". It is in the row pool.
The objects referenced by str8
and str9
are the result of evaluating an expression that is a constant expression. Therefore, they are in the row pool. And in fact, since expressions are evaluated on the "same" string, str8
they str9
refer to the same actual object String
.
The referenced object str7
is the result of evaluating an expression that is NOT a constant expression. Therefore it is NOT in the row pool.
The final reason "Hel" + s
it is not a constant expression is because s
it was not declared as final
.
The thing to remember is that String objects are only allocated in the String pool in two cases:
- If they are the result of evaluating a constant expression, or
- If they are explicitly created using the
String.intern()
.
String literals are a constant expression auxiliary case.
For a more detailed explanation of what a constant expression is, see the Java Language Specification - JLS 15.28 (constant expression) and JLS 4.12.4 (constant variable).
Any row that is not created under one of these circumstances is not in the row pool.
source to share
str7
and str8
are in the pool, but they are not the same string. That is, they are different versions of the same sequence of characters.
Think about the performance you would get if the VM had to scan the entire pool every time a line was created to see if there was another line with the same sequence of characters.
In your new example, you are creating both tags str8
and str9
from the same base strings, so the compiler can more confidently say that the result of each is the same and can reuse that entry in the pool.
source to share
If you call String#intern()
, the compiler scans the pool String
and executes as you seem to expect. I.e
String s = "lo";
String str7 = ("Hel" + s).intern();
String str8 = ("He" + "llo").intern();
System.out.println("str7 == str8 is " + (str7 == str8));
Outputs
str7 == str8 is true
There are two reasons for this behavior.
-
String
is immutable, so use+
requires creating newString
(s) -
String
concatenation is usually implemented withStringBuilder
-new StringBuilder("Hel").append(s).toString()
andnew StringBuilder("He").append("llo").toString()
andStringBuilder
does not scan theString
intern pool .
source to share