0

I have a scenario like this -

 String s = "abc", t="abc"; //LINE 1
 System.out.println(s==t); // definitely it would return true; //LINE 2
 s=s+"d"; t=t+"d";   //LINE 3
 System.out.println(s==t); // output would be false; but why??
 s=s.intern(); t=t.intern();
 System.out.println(s==t); // it would return true;

I wanted to know why the second print statement returned false. Please provide me any reference link which explains the same.

While creating t at line 1; intern was called and it pointed to "abc" why not intern was called at line 3?

default locale
  • 13,035
  • 13
  • 56
  • 62
net user
  • 573
  • 2
  • 8
  • 21
  • 1
    possible duplicate of [How do I compare strings in Java?](http://stackoverflow.com/questions/513832/how-do-i-compare-strings-in-java) – Durandal Aug 05 '14 at 15:55
  • 2
    You seem aware of string interning, and I presume you are aware that using == is generally wrong for string comparison, Could you clarify your question (making clear what you already understand) – Richard Tingle Aug 05 '14 at 15:55
  • [Java Tutorials: Comparing Strings](http://docs.oracle.com/javase/tutorial/java/data/comparestrings.html) – Barranka Aug 05 '14 at 16:04
  • 1
    To those who voted to close as a duplicate: Please retract your votes. The title (and its reference to allocating memory) seems to make it clear that the poster already knows about references. I have seen too many cases lately where readers glance at a question and call it a duplicate based on a few keywords without reading carefully. – ajb Aug 05 '14 at 16:15
  • 1
    Those who voted to close: this question has **nothing** to do with string comparison. OP wants to know about string interning behavior. – Vivin Paliath Aug 05 '14 at 16:23
  • 1
    @VivinPaliath: yes this is what i wanted to know, may be my question was not clear. :( – net user Aug 05 '14 at 16:25

5 Answers5

6

java strings are immutable.

that means that when you do something like s=s+"d" youre actually creating a whole new string, and assigning it to s.

on top of that, the compiler does constant detection and allocation, so that when you write s="abc", t="abc" the compiler re-uses the same reference and your code is effectively s=t="abc"

so you start with the exact same string instance (thanks to compiler optimization) and turn it into 2 identical yet different strings, at which point s==t is false (s.equals(t) would have been true, as it compares the contents and not the address in memory).

next up is intern(). what intern() does is looks up an identical string in the string cache and returns it. if it doesnt find an identical entry it places the argument provided into the cache and returns the argument. so s=s.intern() places s into the string cache and returns it (so s is unchanged) but the following call t=t.intern() actually returns s, so that s==t again.

radai
  • 23,949
  • 10
  • 71
  • 115
  • Let's say the compiler uses Constant Folding optimization, so String s = "abc",t="abc"; s = s + "d"; t = t+"d"; won't this be combined like below String s = "abc" + "d"; which in turn becomes like this String s = "abcd"; Applying Same thing to t as well String t = "abcd"; At this point s and t are literals so i'm expecting s==t to be true while it actually turns out be false. what do you think possibly went wrong :) ? – Arkantos Aug 15 '14 at 18:34
  • @Arkantos - almost all compiler optimizations in java come at runtime. there's very little compile-time optimization. also, in the specific code posted, s and t are accessed (compare) before "d" is appended to them, so such an optimization is illegal in this case. – radai Aug 15 '14 at 19:35
  • ya i know that most of the optimizations are done at runtime based on usage analysis but i expected the constant folding to come into effect when using static javac compiler as well – Arkantos Aug 15 '14 at 19:49
4
String s = "abc", t="abc";

s == t is true because Java automatically interns String literals. In this case the String literal "abc" has been interned and both s and t point to that same instance. Hence s == t is true.

s = s + "d"; t = t + "d";

Strings in Java are immutable. Hence what you are assigning to s and t are two new Strings that have been constructed. Therefore they do not point to the same instance. This is why s == t returns false.

s = s.intern(); t = t.intern();

Here you have forcibly interned the string in s.intern(). Since both s and t contain the same string values, the JVM sees that t is the same and makes it point to the same interned-instance as s. Hence s == t is true.

As a general note, establishing the equality of strings should be done via .equals() and not ==; == only compares references for reference-types and not values.

Vivin Paliath
  • 94,126
  • 40
  • 223
  • 295
  • Thanks and I get that. But when Java is creating new string objects why intern is not called ?? – net user Aug 05 '14 at 16:15
  • @netuser `intern` would require some kind of search (probably a hash table search), and I think that doing this every time a new string were computed would be horribly inefficient. The amount of memory saved wouldn't be worthwhile. You'd be able to use `==` instead of `equals` to compare strings, which would save some time, but in my opinion not nearly enough to outweigh the cost of calling `intern` every time. – ajb Aug 05 '14 at 16:19
  • 1
    @netuser Because that would mean you are needlessly interning numerous strings which may be used one time and then discarded. The GC will clean up those strings now, but if you intern them they will hang around and cannot be garbage collected. – Vivin Paliath Aug 05 '14 at 16:21
  • @VivinPaliath - the string constant pool was moved to the general heap starting with java 7. this means that intern() is no longer a memory leak. – radai Aug 05 '14 at 19:31
4

Strings are "special" Java objects.

The JVM tries to reuse the same references (that's why String s = "abc", t="abc"; causes s and t to point to the same instance), however, when working on instances (like t=t+"d") a new instance gets created, thus, the references are not the same

In order to compare strings you have to use the .equals() method.

intern() causes to create a canonical representation out of the string pool inside the String class ( http://docs.oracle.com/javase/7/docs/api/java/lang/String.html#intern%28%29)

MrTux
  • 32,350
  • 30
  • 109
  • 146
0

Because when you concatenate Strings you generate a new object reference except when they are literal Strings.

Note that the intern of both Strings point to the same literal String object reference.

Luiggi Mendoza
  • 85,076
  • 16
  • 154
  • 332
0

Java Language Specification explicitly covers this particular situation. Here is a quote from chapter 3.10.5. "String Literals":

Moreover, a string literal always refers to the same instance of class String. This is because string literals - or, more generally, strings that are the values of constant expressions (§15.28) - are "interned" so as to share unique instances, using the method String.intern.

As you can see, only constant expressions are interned. So, first four lines of your code are equivalent to:

String s = "abc".intern(), t="abc".intern(); 
System.out.println(s==t); 
s=s+"d".intern(); t=t+"d".intern();   
System.out.println(s==t); 

Expressions s+"d" and t+"d" aren't constant and, thus, aren't interned.

JLS even provides an example with useful notes. Here is the relevant part:

package testPackage;
class Test {
    public static void main(String[] args) {
        String hello = "Hello", lo = "lo";
        System.out.print((hello == ("Hel"+lo)));
    }
}

Output: false
Note: Strings computed by concatenation at run time are newly created and therefore distinct.

default locale
  • 13,035
  • 13
  • 56
  • 62