This week's book giveaway is in the OCMJEA forum.
We're giving away four copies of OCM Java EE 6 Enterprise Architect Exam Guide and have Paul Allen & Joseph Bambara on-line!
See this thread for details.
The moose likes Beginning Java and the fly likes string comparision == Big Moose Saloon
  Search | Java FAQ | Recent Topics | Flagged Topics | Hot Topics | Zero Replies
Register / Login


Win a copy of OCM Java EE 6 Enterprise Architect Exam Guide this week in the OCMJEA forum!
JavaRanch » Java Forums » Java » Beginning Java
Bookmark "string comparision ==" Watch "string comparision ==" New topic
Author

string comparision ==

Rajdeep Biswas
Ranch Hand

Joined: Mar 26, 2012
Posts: 186

Hi,

Why this returns false:

and this returns true:

?
Thanks


The biggest gamble will be to ask a question whose answer you know in that it will challenge your theory | www.TechAspire.blogspot.in
Tim Cooke
Bartender

Joined: Mar 28, 2008
Posts: 971
    
  47

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).


Tim Driven Development
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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.
Tony Docherty
Bartender

Joined: Aug 07, 2007
Posts: 2253
    
  48
Hint: look at lines 7, 12 and 27.

I can only see lines 1 to 11.
Campbell Ritchie
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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
Bartender

Joined: Mar 28, 2008
Posts: 971
    
  47

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

Joined: Aug 07, 2007
Posts: 2253
    
  48
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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
Rajdeep Biswas, have you worked it out yet?
Rajdeep Biswas
Ranch Hand

Joined: Mar 26, 2012
Posts: 186

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.
Henry Wong
author
Sheriff

Joined: Sep 28, 2004
Posts: 18765
    
  40

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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
And have you worked out what the bytecode tells you about the three Strings?
Rajdeep Biswas
Ranch Hand

Joined: Mar 26, 2012
Posts: 186

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

Joined: Mar 26, 2012
Posts: 186

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
Sheriff

Joined: Sep 28, 2004
Posts: 18765
    
  40

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
Sheriff

Joined: Sep 28, 2004
Posts: 18765
    
  40

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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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

Joined: Mar 26, 2012
Posts: 186

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
Sheriff

Joined: Oct 13, 2005
Posts: 38519
    
  23
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
Sheriff

Joined: Sep 28, 2004
Posts: 18765
    
  40

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
Bartender

Joined: Mar 28, 2008
Posts: 971
    
  47

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.
manish ghildiyal
Ranch Hand

Joined: Jan 12, 2013
Posts: 136
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

Joined: Jan 12, 2013
Posts: 136
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
Sheriff

Joined: Sep 28, 2004
Posts: 18765
    
  40

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

Joined: Mar 26, 2012
Posts: 186

Thank you Ranchers, Campbell and Henry for all the information. The post and its explanations have changed my perspective of watching Strings.
 
I agree. Here's the link: http://aspose.com/file-tools
 
subject: string comparision ==