Behavior of String literals is confusing
Asked Answered
F

11

31

The behavior of String literals is very confusing in the code below.

I can understand line 1, line 2, and line 3 are true, but why is line 4 false?

When I print the hashcode of both they are the same.

class Hello
{
   public static void main(String[] args)
   {
      String hello = "Hello", lo = "lo";
      System.out.print((Other1.hello == hello) + " ");     //line 1
      System.out.print((Other1.hello == "Hello") + " ");   //line 2
      System.out.print((hello == ("Hel"+"lo")) + " ");       //line 3
      System.out.print((hello == ("Hel"+lo)) + " ");         //line 4
      System.out.println(hello == ("Hel"+lo).intern());      //line 5
      System.out.println(("Hel"+lo).hashCode());   //hashcode is 69609650 (machine depedent)
      System.out.println("Hello".hashCode());       //hashcode is same WHY ??.
   }
}

class Other1 { static String hello = "Hello"; }

I know that == checks for reference equality and check in the pool for literals. I know equals() is the right way. I want to understand the concept.

I already checked this question, but it doesn't explain clearly.

I would appreciate a complete explanation.

Finedrawn answered 24/5, 2013 at 6:41 Comment(3)
+1 for curiosity.. nice first questionBergmann
Because hashcode equality does not imply object identity. See the Javadoc for Object.hashCode().Johnson
If you knew this, you wouldn't have caused a Resonance Cascade Scenario!Min
J
26

Every compile-time constant expression that is of type String will be put into the String pool.

Essentially that means: if the compiler can (easily) "calculate" the value of the String without running the program, then it will be put into the pool (the rules are slightly more complicated than that and have a few corner cases, see the link above for all the details).

That's true for all the Strings in lines 1-3.

"Hel"+lo is not a compile-time constant expression, because lo is a non-constant variable.

The hash codes are the same, because the hashCode of a String depends only on its content. That's required by the contract of equals() and hashCode().

Julianejuliann answered 24/5, 2013 at 6:51 Comment(10)
ok i want to clearify one more thing , if we do like "Hello" in program or in if condition , thats a new object or literal ??Finedrawn
A literal such as "Hello" is always a compile-time constant expression, therefore this will be taken from the constant pool.Julianejuliann
ok that is fine , let say if the question is how many objects are formed , then , i do if(hello=="Hello") , so when i type "Hello" its not a object and a compile time literal and kept in pool , right ?Finedrawn
"how many objects are formed" is ... a pretty ill-defined question, and frankly only ever interesting to a teacher ;-) Strings in the constant pool are also objects and they also get created at some point. Should you count them or not? I don't know.Julianejuliann
well then i dont how you work , because to make a good performance application you should know how many objects are created at runtime , i use profiler in eclipse , but i want to know how we can count it in compile time as Oracle asks in OCPJP certification ,they want us to know that !! anyway THANKS , ur ans is great and easy to understandFinedrawn
No, to make a good performance application you measure what slows it down and optimize that. I've done that a few times, and I the question if a String is a constant or not never was part of the performance problem. Creating too many String objects by concatenating in a loop could easily be the problem, but that's not (IMHO) related to this question.Julianejuliann
correct for that we need to understand the concept that when it is going to make it an object and constant !! , so if the question is how many objects are created , i am not saying that how many objects are really made in RAM , its not possible for us to know it exactly , but i meant that how we can know that "Hello" is a constant literal or object will be created for that , String s=new String("Hello"); is surely an object , so "Hello" is an object created but at compile time it takes it as constant , does it assumes it as object at compile timeFinedrawn
Do you count one String as one or as two (String itself and its char[])?Coral
How about making lo final.Ruthanneruthe
@AlvinWong ok lets try thatFinedrawn
P
2

Strings computed by concatenation at runtime are newly created and therefore distinct

here is a link to read: http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5

Pekan answered 24/5, 2013 at 6:51 Comment(1)
who downvoted this comment , i think this is right , @JoachimSauer you please check !!Finedrawn
C
2

String object can be created in the following ways:

String str = new String("abcd");  // Using the new operator 
                                  // str is assigned with "abcd" value at compile time.

String str="abcd";         // Using string literal
                           // str is assigned with "abcd" value at compile time.

String str="ab" + "cd";    // Using string constant expression.
                           // str is assigned with "abcd" value at compile time.
String str1 = "cd";
String str = "ab"+str1;    // Using string expression.
                           // str is assigned with "abcd" value at run time only.

and Hashcode will be calculated only at runtime based on the contents of the String objects.

Cuticula answered 24/5, 2013 at 7:29 Comment(0)
W
1

It's because the comipler in this instance is not smart enough to work out that it can burn in the same string literal.

Hashcode needs to always return the same value for strings that are equivelent (calling .equals on it returns true) so will return the same result.

Whicker answered 24/5, 2013 at 6:49 Comment(1)
https://mcmap.net/q/471686/-are-strings-created-with-concatenation-stored-in-the-string-pool same is written hereSabinesabino
S
0

As you already know ... this is just because of reference ...when string comes from the pool it will have same refrence ...but when u do manuplations a new string with new refrence is generated ...

You can check this link for pooling concept

Sabinesabino answered 24/5, 2013 at 6:45 Comment(4)
then how does it apply for hello == ("Hel"+"lo") --> true?Bergmann
Could you please go more in-depth. I'm not sure you are correctElusion
ok in line3 i am doing manipulation "Hel"+"lo" will create new object but it still referred to "Hello" String in pool and when "Hel"+ lo , lo is string variable is added , it will create "Hello" then why this is not referred to same pool literal ??Finedrawn
@Bergmann +1 to your commentMix
M
0

Its because following code

("Hel"+lo)) + " "

is translated internally to

new StringBuilder("Helo").append(new String(lo)).append(new String(" ")).toString()

So you can see that entirely a new String instance is created with help of different String instances. That is why you get false as they point to different memory locations in heap

Mix answered 24/5, 2013 at 6:48 Comment(4)
Then how (hello == ("Hel"+"lo")) + " " --> true? :-) Even here an extra string is appendedBergmann
wrong " " doesnt get appended , i am checking before concatenating ,see the bracesFinedrawn
By the way did you try to see decompiled code ? May be it can shed some light ??Mix
@saury: decompilation can be useful to see the "what" but it rarely answers the "why".Julianejuliann
L
0

The hashCode doesn't have anything to do with an objects reference (The == check is a reference comparator). Its possible to have 2 objects where the hashCode returns the same value, the equals operator returns true, but == returns false. This is when they are 2 different objects, but with the same value.

I believe the reason line 4 is returning false is that it is a value computed at runtime, and thus is a different string instance, with a different reference.

Levo answered 24/5, 2013 at 6:50 Comment(2)
hmm quite well , so at compile time it will check in string pool but when it comes to String manipulation with lo , it will check in runtime , then String hello="Hello" should also be checked at runtime , right ??Finedrawn
String hello="Hello" will be done at compile time, because the compiler knows, before any code is executed, the value ("Hello") that needs to go into that string.Levo
S
0

String literals are saved in a special memory, if they are exactly the same, they are pointed to the same map of memory. If you don't create a literal String, a new object will be created so it won't point to that memory so the reference won't be the same.

The intern() method tells the virtual machine to put it into that shared, string literals map of memory so next time you do that literal, it'll search there and point it.

Scintillate answered 24/5, 2013 at 6:52 Comment(0)
D
0

The difference between line number 3 and 4 are as follows.

•Strings computed by constant expressions are computed at compile time and then treated as if they were literals.

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

The above reference is taken from java spec. Please let me know if you need more clarification.

http://docs.oracle.com/javase/specs/jls/se7/html/jls-3.html#jls-3.10.5

Dynamism answered 24/5, 2013 at 7:18 Comment(1)
hmm i got it , its a mess with Strings!!Finedrawn
C
0

Finally I know the answer !

Read Java SE 8 specification section 15.21.3 Reference Equality Operators == and != (http://docs.oracle.com/javase/specs/jls/se8/html/jls-15.html#jls-15.21.3)

While == may be used to compare references of type String, such an equality test determines whether or not the two operands refer to the same String object.

The result is false if the operands are distinct String objects, even if they contain the same sequence of characters(§3.10.5). The contents of two strings s and t can be tested for equality by the method invocation s.equals(t).

So the following code :

class Test {
    public static void main(String[] args) {
        String hello = "Hello";
        String lo = "lo";
        System.out.println((hello == ("Hel"+lo))); // line 3
    }
}

The expression ("Hel"+lo) in line 3, return the new Strings that computed by concatenation at run time.

*Strings computed by concatenation at run time are newly created and therefore distinct. (http://docs.oracle.com/javase/specs/jls/se8/html/jls-3.html#d5e1634)

So the result of this code:

class Test {
    public static void main(String[] args) {
        String hello = "Hello";
        String lo = "lo";
        System.out.println((hello == ("Hel"+lo))); // line 3
    }
}

would result:

false

Because,

the "Hello" object in this expression:

String hello = "Hello";

and ("Hel"+lo) object in this expression:

System.out.print((hello == ("Hel"+lo)) + " ");

is different, although :

*they both contain the same sequence character, which is "Hello".

*they both have the same hashCode.

*hello.equals(("Hel"+lo)) will return true.

Corunna answered 21/7, 2015 at 15:9 Comment(0)
K
0

System.identityHashCode() would be returned by the default method hashCode(), this is typically implemented by converting the internal address of the object into an integer.

Katlaps answered 31/8, 2017 at 7:44 Comment(2)
String overrides the Object implementation of hashCode.Williswillison
Completely correct,the hash code for a String is computed by the value.Katlaps

© 2022 - 2024 — McMap. All rights reserved.