3

Sorry beforehand for a basic question but can someone please explain 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 that str7 and str8 both point to the same object in String pool specifically because Strings are immutable.

Am I wrong? Are str7 and str8 are both not in the pool but in the heap? Why?

Can you please provide me with example of some String manipulation when the result would be indeed the same immutable string from the string pool?

PS:

String str9 = "He" +"llo";
System.out.println("str8 == str9 is " + (str9 == str8));

outputs true

  • @RajithPemabandu: But the question is specifically about the underlying representation; using `equals` is meant to avoid those very details. – Scott Hunter May 20 '17 at 00:36
  • The reason why str8 == str9 is compiler optimization – Simon May 20 '17 at 00:39
  • Related: [Comparing strings with == which are declared final in Java](https://stackoverflow.com/questions/19418427/comparing-strings-with-which-are-declared-final-in-java) – Pshemo May 20 '17 at 00:49

4 Answers4

4

Your understanding is correct in case of pool where all the literals goes into strings pool.

And the confusion arises when you do the concatenation. Here is the two points to note.

1) If the String resolves at compile time, yes it syncs with String pool and uses the same literals. For ex

String str7 = "Helllo";
String str8 = "He" + "llo";

Note that both are plain literals. Hence no runtime conversions etc.

2) If the String resolves at run time, it resolves to a a new string at runtime and differs with any other other string unless you use .equals method to compare its content.

String str7 = "Hel" + s;
String str8 = "He" + "llo";
System.out.println("str7 == str8 is " + (str7 == str8)); //false

In this case concat strings with (+) operator, JVM returns new StringBuilder(string...).toString() cause one is a plain literal and other is a variable.

Look at the Java language specification

If only one operand expression is of type String, then string conversion (§5.1.11) is performed on the other operand to produce a string at run time.

Questions from comment :

does it mean that in case the string is produced as the product of concatenation of literals then the result is always the same string in the pool?

Yes it is.Remember that concatenation you mean by compile time resolved expression (aka constant expression) and not Runtime.

And when we concatenate string literal with string object then the resulting string is always a new string object built by means of StringBuilder under the hood?

Yes a new String been returned. Attached JVM link confirms you that.

Suresh Atta
  • 120,458
  • 37
  • 198
  • 307
  • 1
    Your answer is perfect, complete and easy to understand thumb up ! – Yahya May 20 '17 at 00:43
  • Thank you Suresh. To make a final point, does it mean that in case the string is produced as the product of concatenation of literals then the result is always the same string in the pool? And when we concatenate string literal with string object then the resulting string is always a new string object built by means of StringBuilder under the hood? – Andrey M. Stepanov May 20 '17 at 00:56
  • @AndreyM.Stepanov Edited my answer with given questions. – Suresh Atta May 20 '17 at 01:28
1

str7 and str8 are both 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 hit you'd get if the VM had to scan the whole pool each time a string was created to see if another string with the same sequence of characters was already present.

In your new example, you are building both str8 and str9 from the same underlying strings, so the compiler can more readily tell the result of each is the same and can re-use that entry in the pool.

Scott Hunter
  • 48,888
  • 12
  • 60
  • 101
  • 3
    Sorry Scott - I'm totally confused and overwhelmed. What does it mean exactly "different versions of the same sequence of characters" residing both in the pool? As to scanning the whole pool I thought that was exactly the point/purpose of having the String pool. – Andrey M. Stepanov May 20 '17 at 00:37
  • 1
    The purpose of the pool is to re-use strings when it has recognized that it can do so; the compiler balances the cost of finding matching strings (which usually fails) with the benefit of re-using them. – Scott Hunter May 20 '17 at 00:41
  • You seem to understand what "different versions of the same sequence of characters" means well enough to argue about it: two distinct strings taking up different locations in memory which happen to have the same characters in the same order. – Scott Hunter May 20 '17 at 00:43
  • 3
    This is incorrect. `str7` is NOT in the string pool. It is not the result of a *constant expression* and it hasn't been explicitly intern'd. – Stephen C May 20 '17 at 00:45
1
String s = "lo";
String str7 = "Hel" + s;
String str8 = "He" + "llo";
String str9 = "He" + "llo";

The object that s refers to is the String object that represents the "lo" literal. It is in the string pool.

The objects that str8 and str9 refer to are the result of evaluating an expression that is a constant expression. Therefore they are in the string pool. And in fact, since the expressions evaluate to "the same" string, str8 and str9 refer to the same actual String object.

The object that str7 refers to is the result of evaluating an expression that is NOT a constant expression. Therefore it is NOT in the string pool.

The ultimate reason why "Hel" + s is not a constant expression here is that s was not declared as final.


The thing to remember here is that String objects are only allocated in the String pool in two circumstances:

  1. If they are the result of evaluating a constant expression, or
  2. If they are explicitly produced using the String.intern() method.

String literals are a sub-case of constant expression.

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 string that is not produced in one of those circumstances not in the string pool.

Graham
  • 7,431
  • 18
  • 59
  • 84
Stephen C
  • 698,415
  • 94
  • 811
  • 1,216
0

If you invoke String#intern(), then the compiler will scan the String pool and perform as you seem to have expected. That is,

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 things causing this behavior

  1. String is immutable, so using + necessitates creating new String(s)
  2. String concatenation is usually implemented with StringBuilder - that is new StringBuilder("Hel").append(s).toString() and new StringBuilder("He").append("llo").toString() and StringBuilder does not scan the String intern pool.
Elliott Frisch
  • 198,278
  • 20
  • 158
  • 249