When "" == s is false but "".equals( s ) is true

20,604

Solution 1

String s = "";
String s2 = someUserInputVariale.toLowercase(); // where the user entered in ""

Something like that would cause s == s2 to evaluate to false.

Lots of code sill create new Strings without exposing the call to new String().

Solution 2

"" == value // yields false

and

"".equals( value ) // yields true

any time the value of the variable value has not been interned. This will be the case if the value is computed at run time. See the JLS section 3.10.5 String Literals for example code illustrating this:

Thus, the test program consisting of the compilation unit (§7.3):

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

and the compilation unit:

package other;
public class Other { static String hello = "Hello"; }

produces the output:

true true true true false true

This example illustrates six points:

  • Literal strings within the same class (§8) in the same package (§7) represent references to the same String object (§4.3.1).
  • Literal strings within different classes in the same package represent references to the same String object.
  • Literal strings within different classes in different packages likewise represent references to the same String object.
  • Strings computed by constant expressions (§15.28) are computed at compile time and then treated as if they were literals.
  • Strings computed at run time are newly created and therefore distinct.
  • The result of explicitly interning a computed string is the same string as any pre-existing literal string with the same contents.

Solution 3

If you can grab a hold of the book Java Puzzlers by Joshua Bloch and Neal Gafter, and look at puzzle 13, "Animal Farm"... he has great advice on this issue. I am going to copy some relevant text:

"You may be aware that compile-time constants of type String are interned [JLS 15.28]. In other words any two constant expressions of type String that designate the same character sequence are represented by identical object references... Your code should rarely, if ever, depend on the interning of string constants. Interning was designed solely to reduce the memory footprint of the virtual machine, not as a tool for programmers... When comparing object references, you should use the equals method in preference to the == operator unless you need to compare object identity rather than value."

That's from the above reference I mentioned... pages 30 - 31 in my book.

Solution 4

Would you expect "abcde".substring(1,2) and "zbcdefgh".substring(1,2) to yield the same String object?

They both yield "equal" sub-strings extracted from two different Strings, but it seems quite reasonable that tehy are different objects, so == sees them as different.

Now consider when the substring has length 0, substring(1, 1). It yields a zero length String, but it's not surprising that the "abcde".substring(1,1) is a different object from "zbcdefgh".substring(1,2) and hence at least one of them is a different object from "".

Solution 5

As I understand it while compiling the Java code to bytecode or while running the program same strings will be referenced to the same object in the most cases to save memory. So sometimes you get away with == comparisons of strings. But this is a compiler optimization you can not rely on.

But then sometimes it happens that the compiler decides to not do this optimization or there is no way for the program to see that the strings are the same and out of the sudden the check fails since you are relying on some underlying optimization voodoo that depends on implementation of the jvm you are using and so on.

So using equals is always the good thing to do. For empty strings there are other possibilities like comparing with length == 0 or if you don't care about backwards compatibility there is string.empty().

Share:
20,604
OscarRyz
Author by

OscarRyz

Software Developer who happens to like writing code. Here are some interesting answers you might like to upvote :") Why java people frequently consume exception silently ? Coding in Other (Spoken) Languages How to create an string from the contents of a file History of Objective-C square brackets (as I remember it) ( visible only to >10k users )

Updated on March 11, 2020

Comments

  • OscarRyz
    OscarRyz about 4 years

    EDIT Thanks for the prompt responses. Please see what the real question is. I have made it bold this time.

    I do understand the difference between == and .equals. So, that's not my question (I actually added some context for that)


    I'm performing the validation below for empty strings:

    if( "" == value ) { 
        // is empty string 
    } 
    

    In the past when fetching values from the db or deserializing objects from another node, this test failed, because the two string instances were indeed different object references, albeit they contained the same data.

    So the fix for those situations was

    if( "".equals( value ) ) {
       // which returns true for all the empty strings
    }
    

    I'm fine with that. That's clearly understood.

    Today this happened once again, but it puzzled me because this time the application is a very small standalone application that doesn't use network at all, so no new string is fetched from the database nor deserizalized from another node.

    So the question is:


    Under which OTHER circumstances:

    "" == value // yields false 
    

    and

    "".equals( value ) // yields true
    

    For a local standalone application?

    I'm pretty sure new String() is not being used in the code.

    And the only way a string reference could be "" is because it is being assigned "" directly in the code (or that's what I thought) like in:

    String a = "";
    String b = a;
    
    assert "" == b ; // this is true 
    

    Somehow (after reading the code more I have a clue) two different empty string object references were created, I would like to know how

    More in the line of jjnguys answer:

    Byte!

    EDIT: Conclusion

    I've found the reason.

    After jjnguy suggestion I was able to look with different eyes to the code.

    The guilty method: StringBuilder.toString()

    A new String object is allocated and initialized to contain the character sequence currently represented by this object.

    Doh!...

        StringBuilder b = new StringBuilder("h");
        b.deleteCharAt( 0 );
        System.out.println( "" == b.toString() ); // prints false
    

    Mystery solved.

    The code uses StringBuilder to deal with an ever growing string. It turns out that at some point somebody did:

     public void someAction( String string ) { 
          if( "" == string ) {
               return;
           }
    
           deleteBankAccount( string );
     }
    

    and use

     someAction( myBuilder.toString() ); // bug introduced. 
    

    p.s. Have I read too much CodingHorror lately? Or why do I feel the need to add some funny animal pictures here?

  • Michael Donohue
    Michael Donohue almost 15 years
    I don't think this analogy really clarifies anything
  • Cortney Thomas
    Cortney Thomas almost 15 years
    Anytime a string is generated/created/manipulated at runtime, it has the potential to happen. I'm guessing the VM can create a new String for the string pool as it sees fit, even though for optimization, it would be better to use an existing String.
  • Tom
    Tom almost 15 years
    In other words... use equals because it's what you mean... and it is what you mean.
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 15 years
    I think this is a good answer. clearly one would expect for it that == yields false but .equals yields true. And that's what the questioner actually asked, not "what's the diff between == and equals" what most other answered him
  • djna
    djna almost 15 years
    I've tried to spell it out a bit more. When teaching I prefer to use questions rather than answers. Guess that doesn't work quite so easily in text.
  • Vishy
    Vishy almost 15 years
    I would suggest this too, unless the string could be null. ;)
  • Steve Jessop
    Steve Jessop almost 15 years
    No, I like it. "We Java programmers don't even know how to SPELL optimyz... optimo... look, the JIT does it for us alright?"
  • jjnguy
    jjnguy almost 15 years
    haha. The sad part is, Firefox corrects me, but I'm just too lazy to fix it. I need auto complete for regular text.
  • OscarRyz
    OscarRyz almost 15 years
    Although not the only right answer, this lead me to find out the reason. Thanks. ..
  • Johannes Schaub - litb
    Johannes Schaub - litb almost 15 years
    I don't understand the third paragraph though.
  • OscarRyz
    OscarRyz almost 15 years
    Yeah. Well, for applications using network it quite common to fail, but for self contained code it's very strange. The google query is nice!! :)
  • Angel O'Sphere
    Angel O'Sphere over 12 years
    I don't know since when but in Java 1.5 Strings that are constructed via substring calls share the underlying char array. That means "abcde" and "abcde".substring(1,2), lets call this BC1, hold the exact same one and only "abcde" char array. Now looking at "zbcdefgh".substring(1,2) holds a references tho the shared array "zbcdefgh", lets call this BC2. Now you see that BC1 and BC2 never can be "==" but need to be completely different objects. After all BC1 contains still "abcde" and BC2 still contains "zbcdefgh".
  • ComputerDruid
    ComputerDruid almost 10 years
    Unfortunately, the link is dead