4

I know this has been covered but I've seen inconsistent arguments here on SO.

So if I have:

String a = "apple2e";
String b = "apple2e";

System.out.println("a==b? " + a == b);

I get FALSE.

As I understand it, it's because a and b are two different references to the same object (apple2e).

So I would have something like:

a (reference_id 123) ------
                           ---------  "apple2e"
b (reference_id 456) ------

Now, if I just want to compare the contents of the two strings, I would use a.equals(b)

Does that mean that the JVM is simply returning if the two references are pointing to the same object? So it's not really doing a character-by-character comparison?

Thanks

EDIT

Hold the phones. Thanks delnan for pointing out the + precedence!!!

When I change it to:

System.out.println(a == b);

I indeed get true.

This makes more sense.

EDIT 2

I can't believe I didn't catch that. lol

I was doing:

"a==b? " + a == b

Which translates to

"a==b? apple2e" == "apple2e"

No wonder it was false!!

cbmeeks
  • 11,248
  • 22
  • 85
  • 136
  • 5
    Isn't it slightly strange that your output is just `false` instead of `a==b? false`? `+` has precedence over `==`, so you are actually concating `a` with the string literal and comparing the result of that (`"a==b? apple2e"`) with `b` in your example! –  Jul 28 '11 at 20:07
  • In this case you should get true instead of false, since they both refer to the same String literal. You would need String b = new String("apple2e"); to get the print statement to return false. Regardless of this always use a.equals(b) if you want to compare contents – Michael Krussel Jul 28 '11 at 20:09
  • I just ran it and I get `false`. – cbmeeks Jul 28 '11 at 20:10
  • See `.intern()`: and `==` works as expected. – Pindatjuh Jul 28 '11 at 20:10
  • 4
    Yes, you (just like everyone else with a functioning Java implementation) get `false` for the reasons outlined in my first comment. Put parens around the `a + b` and it outputs `a==b? true` as expected. –  Jul 28 '11 at 20:11
  • You are correct! See my edit. Yeah, I didn't think about the `+` precedence!! – cbmeeks Jul 28 '11 at 20:13

7 Answers7

7

As I understand it, it's because a and b are two different references to the same object (apple2e).

Because of string interning, and only because of string interning a and b are different references to the same String object.


Unfortunately, your code does not do what you think it does. Try this:

String a = "apple2e";
String b = "apple2e";

System.out.println("a==b? " + a == b);    // "false"
System.out.println("a==b? " + (a == b));  // "a==b? true"

Java automatically interns all string literals. That is why the second sysout prints what it does. The first sysout prints only "false" because string concatenation (+) has higher precedence than ==, so it's equivalent to this:

System.out.println("a==b? apple2e" == "apple2e");

I don't think that's what you meant to test!

This, on the other hand, will give you two separate String instances:

String a = new String("apple2e");
String b = new String("apple2e");

System.out.println("a==b? " + (a == b));  // "a==b? false"

Which would schematically look like

a (reference_id 123) ---------------  "apple2e"

b (reference_id 456) ---------------  "apple2e"

and can be reduced to the original situation using String#intern():

String a = new String("apple2e").intern();
String b = new String("apple2e").intern();

System.out.println("a==b? " + (a == b));  // "a==b? true"

e.g.

a (reference_id 123) ------+
                           +---------  "apple2e"
b (reference_id 456) ------+
Matt Ball
  • 354,903
  • 100
  • 647
  • 710
  • So there are **two** `apple2e' objects/values in memory? – cbmeeks Jul 28 '11 at 20:06
  • 2
    Not necessarily. One should generally assume that, but for string literals in particular it isn't always true (sadly, this has always caused much confusion). OP is getting `false` output for a different reason. See http://www.ideone.com/uaTf7 for an example where the two *are* the same. –  Jul 28 '11 at 20:09
  • 1
    There should only be one "apple2e" in memory because it is a compile time constant. – emory Jul 28 '11 at 20:10
  • @cbmeeks unfortunately, [string interning](http://en.wikipedia.org/wiki/String_interning) makes this a bit more gnarly to reason about. Every string literal is interned in Java. See my edit. – Matt Ball Jul 28 '11 at 20:11
  • 2
    Note that string interning only works because strings in Java are immutable. Nothing can change the value, so you can simply have two entirely different parts of the program point to the same string, without worrying that one changes the one also used by the other. Of course, this is not always smart - you don't want to check each and every string instance when you create a new one for equality to save some storage. So generally, when you compile things, everything is interned, but after that you don't know. So always use .equals() especially since the first instruction is '==' anyways. – Maarten Bodewes Jul 28 '11 at 20:26
  • The initial part of your answer is misleading. The emboldened "modulo string interning" seems to contradict your previous words. – Atreys Jul 28 '11 at 21:19
  • @Atreys thanks for calling me out on that one, you are right. It was a sloppy edit. Take a look at my changes. – Matt Ball Jul 29 '11 at 02:56
6

Your first example is actually true. The expression in the println just isn't doing what you meant.

String a = "apple2e";
String b = "apple2e";

System.out.println("a==b? " + (a == b));

Will print a==b? true note the all important grouping!

You are correct that a and b are two different references to the same object. This is exactly why == returns true when you test it! == tests if two different pointers point to the same location in memory. Both a and b are references to the same place in memory that holds the interned compile time "apple2e" string.

Now if you did this

String a = new String("apple2e");
String b = new String("apple2e");

System.out.println("a==b? " + (a == b));

It actually will print a==b? false. Because you made two different objects in memory, a and b point to different places.

Affe
  • 47,174
  • 11
  • 83
  • 83
5

'a' and 'b' are the same reference, the problem is you are not comparing a and b. You should expect

a==b? false

but you just get

false

This is because + takes precedence over == so what you have is

System.out.println(("a==b? " + a) == b);

In the same way you would expect the following to print true.

System.out.println(1 + 2 == 3);

What you need is

System.out.println("a==b? " + (a == b));
Peter Lawrey
  • 525,659
  • 79
  • 751
  • 1,130
4

a == b looks to see if the two objects point to the same object in memory. Because they don't, even though their internals are the same, a == b will be false. 1 == 1 will be true though, because integers are primitive variables, while Strings are actually objects.

a.equals(b) is an instance method that internally checks to see if the two strings have the same characters.

If you did:

String a = "hello";
String b = a;

Then a == b would be true.

MrDanA
  • 11,489
  • 2
  • 36
  • 47
1

apple2e is not an object. It is the value pointed by the reference.

String a = "apple2e";
String b = "apple2e";

Here, a, b are objects ( references ) to the value apple2e stored at two different locations.

Mahesh
  • 34,573
  • 20
  • 89
  • 115
  • 1
    `"apple2e"` is a `String` literal. If you accept that a `String` literal is a `String`, then `"apple2e"` **is** an object since strings are objects - they inherit from `java.lang.Object`. – Matt Ball Jul 28 '11 at 20:06
  • sorry, but both are stored at the SAME location! JLS 3.10.5 – user85421 Jul 28 '11 at 20:27
  • @Carlos - Other answers are interesting. Let me go through them to find out why. Thanks. – Mahesh Jul 28 '11 at 20:29
1

a and b are two different references to the same object -- PROBABLY

"a==b? " + a

evaluates to the string "a==b? apple2e" which is definitely not the same (either == or equals) as the string "apple2e"

So your code:

System.out.println("a==b? " + a == b);

is just testing if "a==b? apple2e" is the same as "apple2e" which it is not (no matter how you define same).

emory
  • 10,725
  • 2
  • 30
  • 58
  • Why do you think a and b are two different references to the same object? What makes you think they are referencing the same object? Just because they are equal? They are not primitives... – Joe Phillips Jul 28 '11 at 20:13
  • @Joe They are probably two different references to the same object because of string interning - http://en.wikipedia.org/wiki/String_interning. That is usually how it works, but it is not actually guaranteed to be two different references to the same object - so in any case it is better to use the equals method for comparison. – emory Jul 28 '11 at 20:18
  • NOT PROBABLY, FOR SURE the same object! Since they are literal Strings they ARE the same object - Java Language Specification 3.10.5: `...String literals-or, more generally, strings that are the values of constant expressions (§15.28)-are "interned" so as to share unique instances,...` – user85421 Jul 28 '11 at 20:22
  • Very interesting. And this works because Strings are immutable. – Joe Phillips Jul 28 '11 at 20:29
1

If you change to the following: System.out.println("a==b? " + (a == b));

You get a==b? true

Whats happening here is that a and b both point to an internal cache of Strings that is maintained by the String class.

Here is the implementation of String.equals()

public boolean equals(Object anObject) {
if (this == anObject) {
    return true;
}
if (anObject instanceof String) {
    String anotherString = (String)anObject;
    int n = count;
    if (n == anotherString.count) {
    char v1[] = value;
    char v2[] = anotherString.value;
    int i = offset;
    int j = anotherString.offset;
    while (n-- != 0) {
        if (v1[i++] != v2[j++])
        return false;
    }
    return true;
    }
}
return false;
}

anObject must be a String in order to return true, if it is a reference to the same Object, then it returns true immediately. Otherwise as you can see, is is doing a character by character comparison.

crowne
  • 8,456
  • 3
  • 35
  • 50