• Post Reply Bookmark Topic Watch Topic
  • New Topic
programming forums Java Mobile Certification Databases Caching Books Engineering Micro Controllers OS Languages Paradigms IDEs Build Tools Frameworks Application Servers Open Source This Site Careers Other Pie Elite all forums
this forum made possible by our volunteer staff, including ...
Marshals:
  • Campbell Ritchie
  • Jeanne Boyarsky
  • Ron McLeod
  • Paul Clapham
  • Liutauras Vilda
Sheriffs:
  • paul wheaton
  • Rob Spoor
  • Devaka Cooray
Saloon Keepers:
  • Stephan van Hulst
  • Tim Holloway
  • Carey Brown
  • Frits Walraven
  • Tim Moores
Bartenders:
  • Mikalai Zaikin

string comparision ==

 
Ranch Hand
Posts: 233
1
Eclipse IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi,

Why this returns false:

and this returns true:

?
Thanks
 
Sheriff
Posts: 5555
326
IntelliJ IDE Python Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
It's all to do with how String objects are stored in a pool at runtime so two String variables can point to the same Object. This is why your == comparison can return true in some cases. I do not fully understand how it works and don't tend to concern myself too much as it's mostly academic.

However, in practice I would not recommend that you expect a String comparison using == to ever return true unless you are explicitly interested in whether they are the same Object. For String comparison always use .equals(String str).
 
Marshal
Posts: 79177
377
  • Likes 3
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Because you are inappropriately using the == operator. It should not be used on reference types, except when they are enum elements, or you specifically need to check they are the same object for example inside an equals() method.
As you have seen the behaviour of the == operator is counter‑intuitive, giving different results depending what sort of String you have.

I shall give you a hint. Let's put your code into a method, and then compile it.

javac WordDemo.java
javap -c WordDemo

Hint: look at lines 7, 12 and 27.
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Tim Cooke wrote: . . . I do not fully understand how it works and don't tend to concern myself too much as it's mostly academic. . . .

It's actually quite simple; you may get a hint from looking at lines 7 12 and 27 in my last example.

I am keeping quiet about the full details for the time being.
 
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Hint: look at lines 7, 12 and 27.


I can only see lines 1 to 11.
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I meant 7 12 27 after running the javap -c call. Sorry if I wasn't clear. Those may not actually be line numbers, but they are the instruction numbers which appear on the terminal window.
 
Tim Cooke
Sheriff
Posts: 5555
326
IntelliJ IDE Python Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:It's actually quite simple; you may get a hint from looking at lines 7 12 and 27 in my last example.


Fascinating. I've never done this before, disassembling a class. I don't understand all of it but I think I see what you're getting at with how the Strings are stored.

Thanks for that. Learnt something new today!
 
Tony Docherty
Bartender
Posts: 3323
86
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote:I meant 7 12 27 after running the javap -c call. Sorry if I wasn't clear.


No it was me not reading the instructions properly, sorry.
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
You're welcome
You can tell from the three Strings' values shown in that printout what values they have at that stage in the development of that class.
What you are seeing is the expansion of the bytecode. If you ever learn assembler, you will find the two look very similar.
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Rajdeep Biswas, have you worked it out yet?
 
Rajdeep Biswas
Ranch Hand
Posts: 233
1
Eclipse IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Hi,

Sorry for late reply Campbell and others for late reply. This is going to be awesome learning, not now though, as nothing solid grasped.

string "length: " was created --> String pig's length was calculated ---> StringBuilder.append() object created and method attached and the int length passed ---> toString() --> "length: 10"

But can you explain more on the javap -c output, and relate this to the String pooling (some more insight)..

Thanks All,
Raj

PS. == was used intentionally to check the reference for stirring string pooling concepts in my mind.
 
author
Posts: 23951
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rajdeep Biswas wrote:
Sorry for late reply Campbell and others for late reply. This is going to be awesome learning, not now though, as nothing solid grasped.

string "length: " was created --> String pig's length was calculated ---> StringBuilder.append() object created and method attached and the int length passed ---> toString() --> "length: 10"

But can you explain more on the javap -c output, and relate this to the String pooling (some more insight)..

Thanks All,
Raj

PS. == was used intentionally to check the reference for stirring string pooling concepts in my mind.




Personally, I don't like the direction of where this topic is going. The question was "why this output?". And the answer was "because the compiler is doing this". While this answers the question, it leads to the follow up question of "why is the compiler doing that?", which is not addressed here.


BTW... I don't mean to imply that this topic direction is useless or bad. I am just pointing out the follow up questions / confusion coming (or arguably, has occurred).

Henry
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
This is about the only thread I am watching at the moment; I have too much on my plate to be able to do much more.
The reason for inspecting the bytecode is that it allows you to see what the compiler is seeing, particularly the three Strings in lines (I think) 7 12 and 27.
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
And have you worked out what the bytecode tells you about the three Strings?
 
Rajdeep Biswas
Ranch Hand
Posts: 233
1
Eclipse IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Before the output, I thought that pig, dog and dog2 will all share the same String literal due to String pooling, but I can not find anything in that regard in the javap -c output. Need more enlightenment. All I have found is that
string "length: " was created --> String pig's length was calculated ---> StringBuilder.append() object created and method attached and the int length passed ---> toString() --> "length: 10"
 
Rajdeep Biswas
Ranch Hand
Posts: 233
1
Eclipse IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Somewhere I learned that when we return a string from a method, it create a new string in memory.
But here, for dog, "length: " was present. 10 was calculated from method, and this was subjected to toString() method,
and that gave me "10" from 10.
now, after concatenating, i got "length: 10" and a same literal already exists for pig in the pool. so why a new object was created?

Edit: I think StringBuilder was used when I called the length() method and so a new object was created, and dog is referring to the newer one. pig refers to original "length: 10" literal only. Am I right?
 
Henry Wong
author
Posts: 23951
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rajdeep Biswas wrote:Somewhere I learned that when we return a string from a method, it create a new string in memory.
But here, for dog, "length: " was present. 10 was calculated from method, and this was subjected to toString() method,
and that gave me "10" from 10.
now, after concatenating, i got "length: 10" and a same literal already exists for pig in the pool. so why a new object was created?

Edit: I think StringBuilder was used when I called the length() method and so a new object was created, and dog is referring to the newer one. pig refers to original "length: 10" literal only. Am I right?




While this is a detailed response, keep in mind that we have no idea of the example in use here -- the previous posts have more than one example, so can't really follow what you are saying very well.

Henry
 
Henry Wong
author
Posts: 23951
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rajdeep Biswas wrote:
and this returns true:

?




First, a string literal doesn't necessary mean that it is in the string pool. Take this part of the code...



Keep in mind that it is string compile time constants, that are needed by the class, that the class will automatically intern into the string pool. In this line, "length: " and "10" are both string literals, and hence, both string compile time constants. However, the concatenation of two compile time constants is also a compile time constant -- hence, "length: " +"10" is a compile time constant. With this line, the concatenated result of "length: 10" is interned into the string pool. The "length: " and "10" string literals are not needed by the class and hence, not interned.

So, in this example, pig and dog both point to the same interned "length: 10" string, while "length: " and "10" are not in the string pool.

Henry

 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
But in the other example "length: " was a String compile-time literal. The compiler did not know about the 10, so the "length: 10" was not a compile-time literal. It was created as a different String object later when the code was executed.
You can tell that two of the Strings in the javap printout (12 and 27, I think) were length: 10 (compile-time literals) but the one in line 7 was only length: .
Also note that the "10" String (I think) has disappeared from the javap; although it is interned as a compile-time constant, it is not needed by this code.
 
Rajdeep Biswas
Ranch Hand
Posts: 233
1
Eclipse IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Although I reckon that the idea of compile-time literal is justified, but still the idea of string pooling in this context is vague in my understanding, as it is a concept of object reference in my example.

This is how I thought:

"length: 10" was created in heap, say 1010 address, placed in pool, so that pig -> 1010.

"length: " was created in heap, say 1011 address, placed in pool.
String pig's length was calculated ---> StringBuilder.append() : object created and method attached and the int length 10 passed ---> toString() -->"10"
"10" was placed in the pool in heap, say 1012 address.
the + operator resulted in "length: 10", and this literal is already present in the pool at 1010.
Since, new wasn't used, why should the JVM go for a new instance.
Thats how I thought
dog -> 1010, and

but it returned false.

Please help my thought.

Raj
 
Campbell Ritchie
Marshal
Posts: 79177
377
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rajdeep Biswas wrote: . . .
"length: " was created in heap, say 1011 address, placed in pool.
String pig's length was calculated ---> StringBuilder.append() : object created and method attached and the int length 10 passed . . .

No, you are mistaken on that point. The compile‑time literal Strings are worked out by the compiler at compile‑time, and you can see them in the bytecode. In that case "length: ", as in my line 7. Remember that bytecode has never been executed, so there is nothing loaded. Also the ...length() method has not been executed at that stage.
The Java Language Specification is a little vague but it would appear that a String "10" is created at runtime (not when I saw the code, because I never executed it) and catenated and a new String is created. The JLS is clear and definite about a new String. It does not say that the String "10" is stored anywhere, nor does it say that the catenated String is interned. You may be able to learn more from that bytecode.
Also how do you know that a StringBuilder was used for the catenation? It doesn' say anything about StringBuilders in that JLS link. Now, we think that the Sun/Oracle JVM uses a StringBuilder as an optimisation for the + operator, and a StringBuilder was mentioned in the bytecode, I think. But other JVMs might use a different technique.

The reason you got false is that the new String length: 10 was not created until runtime, after the compile‑time constant String had been interned and it is a different object. And the conclusion we draw is:-




Don't use ==.
 
Henry Wong
author
Posts: 23951
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Likes 1
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Rajdeep Biswas wrote:
Why this returns false:




Since I explained the 2nd example in terms of compile time constants, I guess I can explain the first example too...



First, obviously, the "length: 10" literal is a compile time constant. Second, the pig reference is also a compile time constant -- specifically it is a constant variable, which is a compile time constant.

And here is the interesting part, it is not clear if this string needs to be interned yet. Since the pig reference is a compile time constant, there isn't any variable space allocated for it -- meaning there isn't actually a pig instance variable. And the assignment won't actually happen at runtime .... of course, the string will have to be interned because the pig reference is used later in the code (specifically, the next line).



Okay, the pig reference is a compile time constant, but pig.length() is not. The reason is because method calls of compile time constants is never a compile time constant. Furthermore, we also know that it is not interned because the result is not a string.


Next, since, pig.length() is not a compile time constant, then the "length: " + pig.length() expression is also not a compile time constant. The concatenation of a compile time constant and not a compile time constant is not a compile constant.

And since the expression is not a compile time constant, the compiler will generate the code to do the concatenation -- meaning StringBuilder, call to length(), call to append(), toString(), etc.

And finally, since the expression is not a compile time constant, the dog reference variable is not a compile time constant. It is a real variable -- that will be allocated, initialized, etc.



And since pig is a compile time constant referring to an interned string, while dog is referring to a calculated string (that is not interned), the result of this print statement is false.

Hope this helps,
Henry
 
Tim Cooke
Sheriff
Posts: 5555
326
IntelliJ IDE Python Java Linux
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
I've been following this thread for the last few days and previously I only had a loose grasp on this topic, but now I'm much more clued in. Many thanks to Campbell and Henry for their super explanations.

However... I'm still never going to use == for String comparisons. Did I mention that before? I think I did.
 
Ranch Hand
Posts: 136
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Campbell Ritchie wrote: although it is interned as a compile-time constant, it is not needed by this code.




Hi Ritchie,

I am keenly following the discussion but TBH not getting much of it.

To start with, above quoted line was not clear to me. Does it mean that an interned object is removed from memory if not needed by code?
And what does it mean to say "not needed by code"?

Manish
 
manish ghildiyal
Ranch Hand
Posts: 136
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

Henry Wong wrote:And here is the interesting part, it is not clear if this string needs to be interned yet



Hi Wong,

Does it mean that only those literals are interned which are reused in program?

So if my program consists of only:

String str = "abc";

....then "abc" won't be interned.

But if my program is as:
String str = "abc";
String str1 = "abc";

....now "abc" would be interned.

Manish
 
Henry Wong
author
Posts: 23951
142
jQuery Eclipse IDE Firefox Browser VI Editor C++ Chrome Java Linux Windows
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator

manish ghildiyal wrote:

Henry Wong wrote:And here is the interesting part, it is not clear if this string needs to be interned yet



Does it mean that only those literals are interned which are reused in program?

So if my program consists of only:

String str = "abc";

....then "abc" won't be interned.

But if my program is as:
String str = "abc";
String str1 = "abc";

....now "abc" would be interned.




In your example...



The str variable is not a compile time constant. It actually exists. So, the string value will be interned. (And BTW, this part is an implementation detail, it may not be true for all versions. So perhaps, I shouldn't have mentioned it in the previous post).

Henry
 
Rajdeep Biswas
Ranch Hand
Posts: 233
1
Eclipse IDE Opera Java
  • Mark post as helpful
  • send pies
    Number of slices to send:
    Optional 'thank-you' note:
  • Quote
  • Report post to moderator
Thank you Ranchers, Campbell and Henry for all the information. The post and its explanations have changed my perspective of watching Strings.
 
With a little knowledge, a cast iron skillet is non-stick and lasts a lifetime.
reply
    Bookmark Topic Watch Topic
  • New Topic