5

I found an interesting case while testing with string creation and checking their hashcode.

In first case i created string using copy constructor:

public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {

        String s1 = new String("myTestString");

        String s3 = s1.intern();

        System.out.println("S1: " + System.identityHashCode(s1) + "  S3:"
                + System.identityHashCode(s3));
    }


}

Output of above code is:

S1: 816115710 S3:478684581

This is expected output as interned string picks the reference from String pool whereas s1 picks reference of new object. So their identity hash code is different.

Now if i create String using char array then i see some strange behavior:

public class Test {

    /**
     * @param args
     */
    public static void main(String[] args) {

        char[] c1 = { 'm', 'y', 'T', 'e', 's', 't', 'S', 't', 'r', 'i', 'n',
                'g' };

        String s5 = new String(c1);

        String s6 = s5.intern();

        System.out.println("S5: " + System.identityHashCode(s5) + "  S6:"
                + System.identityHashCode(s6));
    }

}

Output of above code is:

S5: 816115710 S6:816115710

This is an unexpected output. How can interned String and new String object have same identityhashcode??

Any ideas?

Lokesh
  • 7,810
  • 6
  • 48
  • 78
  • Take a look at http://stackoverflow.com/questions/1063068/how-does-the-jvm-ensure-that-system-identityhashcode-will-never-change – Igor S. May 19 '13 at 09:30
  • @IgorS.: How is it linked to my question? – Lokesh May 19 '13 at 09:31
  • 1
    "Multiple objects can have the same identity hash code. That is the nature of hash codes." – Igor S. May 19 '13 at 09:34
  • @IgorS.: Yes they can have only in certain scenarios. In above scenario they should not have. – Lokesh May 19 '13 at 09:35
  • It should. To keep memory usage low - VM clearly use lazy loading, because you didn't do anything with strings it pointing to the same memory address. First case is probably different, because of characters encoding or/and string end character. – Igor S. May 19 '13 at 09:41
  • Although i disagree with your suggestion but even if we take that approach then s1 and s3 should also have same identity hash code, isn't it? – Lokesh May 19 '13 at 09:44

1 Answers1

3

In the first case, the myTestString literal is on the pool before you call intern, whereas in the second case it is not so your String s5 is put in the pool directly.

If we go through your examples step by step, this is what happens:

  • String s1 = new String("myTestString"); => the use of a String literal creates a String myTestString in the pool (let's call it s0), and a new String s1 is also created, which is not in the pool.
  • String s3 = s1.intern(); => checks if there is an equivalent String in the pool and finds s0. Now s3 and s0 refer to the same instance (i.e. s3 == s0 is true, but s1 != s0).

In your second example:

  • String s5 = new String(c1); creates a new String, which is not in the pool
  • String s6 = s5.intern(); checks if myTestString is in the pool but can't find it, so the call to intern creates a new String reference in the pool that refers to the same String as s5. So s6 == s5 is true.

Finally you can run these two programs to confirm my explanation (the second one prints true three times):

public static void main(String[] args) {
    String s1 = new String("myTestString");
    String s3 = s1.intern();
    System.out.println("myTestString" == s1);
    System.out.println(s3 == s1);
    System.out.println("myTestString" == s3);
}

public static void main(String[] args) {
    String s1 = new String(new char[] {'m', 'y', 'T', 'e', 's', 't', 'S', 't', 'r', 'i', 'n', 'g'});
    String s3 = s1.intern();
    System.out.println("myTestString" == s3);
    System.out.println("myTestString" == s1);
    System.out.println(s3 == s1);
}
assylias
  • 321,522
  • 82
  • 660
  • 783
  • Why would s5 be put into pool directly? I am using new operator to create it. – Lokesh May 19 '13 at 09:36
  • It is put in the pool when you call intern because that specific string was not already in the pool. How you created the string is irrelevant in that case. – assylias May 19 '13 at 10:25
  • same should also apply to s1, s3 then. Why is that different? – Lokesh May 19 '13 at 10:26
  • @loki I have added more details. The difference is that in your first example there is a String literal which goes to the pool before your program runs. – assylias May 19 '13 at 10:51
  • +1 good point, didn't thought about explaining the "expected result" ;) – Trinimon May 19 '13 at 11:02
  • Well explained. But as per your explanation, when i call s5.intern(); then reference of s5 also changes. There is no such mention in javadoc, it only talks about return value of intern method. Do you any place where this is documented? – Lokesh May 19 '13 at 11:10
  • @loki No it does not change. The pool is simply a "list" of references, that refer to some strings. In the second case, the reference used is s5. In other words, calling `str.intern()` does something like: `if (there is a string s_p in the pool such that s_p.equals(str)) { return s_p; } else { return str; }`. – assylias May 19 '13 at 11:12
  • Perfect!! I can relate this to javadoc now. Thanks. – Lokesh May 19 '13 at 11:14