7

Consider two references of type Integer that call the static factory method valueOf as shown below:-

    Integer a = Integer.valueOf("10"); 
    Integer b = Integer.valueOf("10"); 

Considering that Integer is immutable, is it ok to compare a and b using == instead of using equals method. I am guessing that the valueOf method makes sure that only one instance of Integer with the value 10 is created and a reference to this instance is returned for every Integer created with a value 10.

In general, is it ok to compare two references of an immutable class that are created using a call to the same static factory method by using == instead of equals?

Edit: The Integer class was used just as an example. I am aware thar Intgers upto 127 will return true if they are compared using ==. What i need to know is that when l create my own immutable class, say MyImmutable with a method create() that will ensure that no duplicate MyImmutable objects are created, will it be ok if I compare 2 MyImmutable references created using the create method by using == instead of equals.

Chetan Kinger
  • 15,069
  • 6
  • 45
  • 82
  • Please read [this](http://www.javabeat.net/qna/13-what-is-difference-between-equals-and-/) – Fahim Parkar Jun 10 '12 at 17:45
  • 2
    Immutability just implies that they cannot change. Special care needs to be taken with caching (no `new`) to ensure that the same values are returned for the same input parameters. – Thorbjørn Ravn Andersen Jun 10 '12 at 18:05
  • You can use `==` instead of equals for types where equals has not been overridden. As this could change in the future, equals is almost always better. The only time I would say `==` is better is with types like `enum` which might be `null` and using `==` simplifies the code in this case. – Peter Lawrey Jun 11 '12 at 07:08
  • Using == over equals improves performance. If a method in an immutable class guarantees that the references it returns are such that a.equals(b) is true only if a == b, then I don't see a reason why == should be avoided. – Chetan Kinger Jun 11 '12 at 08:42

5 Answers5

9

No, that's not safe in general. The == operator compares the references, not the values.

Using == happens to work for integers between -128 and 127, but not for other integers. The following code demonstrates that == won't always work:

Integer a = Integer.valueOf(10); 
Integer b = Integer.valueOf(10); 
System.out.println(a == b);

true

Integer c = Integer.valueOf(1000); 
Integer d = Integer.valueOf(1000); 
System.out.println(c == d);

false

See it working online: ideone

The explanation for this behaviour lies in the implementation of Integer.valueOf:

public static Integer valueOf(int i) {
     final int offset = 128;
     if (i >= -128 && i <= 127) { // must cache
         return IntegerCache.cache[i + offset];
     }
     return new Integer(i);
 }

source

Not also that the standard requires that boxing integers for small inputs (-128 to 127) gives objects with equal references.

5.1.7 Boxing Conversion

If the value p being boxed is true, false, a byte, a char in the range \u0000 to \u007f, or an int or short number between -128 and 127, then let r1 and r2 be the results of any two boxing conversions of p. It is always the case that r1 == r2.

However the standard makes no such guarantees for integers outside this range.


In general, is it ok to compare two references of an immutable class that are created using a call to the same static factory method by using == instead of equals?

As shown above, it won't work in general. But if you ensure that two immutable objects with the same value always have the same reference, then yes, it could work. However there are some rules you must follow carefully:

  • The constructor must not be public.
  • Every object you create via the static method must be cached.
  • Every time you are asked to create an object you must first check the cache to see if you have already created an object with the same value.
Community
  • 1
  • 1
Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • My questionbis not about the Integer class. I am hoping to receive a general answer. – Chetan Kinger Jun 10 '12 at 18:14
  • I am sorry. I missed the last part. I am logged in through my galaxy S3 and its not the same as a desktop/laptop. : ) – Chetan Kinger Jun 10 '12 at 18:27
  • This is indeed the answer I was looking for. Thanks a lot! – Chetan Kinger Jun 10 '12 at 18:31
  • 1
    I would add in more recent versions of Java 6 and in 7, the maximum Integer cached is tuned/changes. This means that 1000 or even 10000 may or may not be cached. I tend to use -129 as an example instead as only the maximum positive number is changed (or you could use Long which is limited to 127L currently) – Peter Lawrey Jun 11 '12 at 07:05
7

== and equals() is fundamentally different.

You should read this post for more details:

Difference between Equals/equals and == operator?

It has nothing to do with immutable objects.

Community
  • 1
  • 1
Lai Xin Chu
  • 2,462
  • 15
  • 29
  • 1
    I know the difference between equals and == very well. Please try understanding my question before you state the obvious. – Chetan Kinger Jun 10 '12 at 18:05
  • 1
    Ok. I will give you an example where it will be okay to use == to compare. For example, I have an application which has a User object. I ensure that I only instantiate one instance for each user ie. When I call for user with id 123, there will always be only one User object existing in my entire application with id 123. In this case, using == is acceptable to compare 2 User objects to see if they are the same user. – Lai Xin Chu Jun 10 '12 at 18:08
  • Otherwise, its generally not okay to compare 2 objects using == Sorry if i stated the obvious, but your question before the edit was a different direction altogether. – Lai Xin Chu Jun 10 '12 at 18:10
  • Thats not a problem. Thank you for the example. – Chetan Kinger Jun 10 '12 at 18:17
  • 1
    I lost patience after reading obvious answers. Although you are right. I will read the answers properly once again. – Chetan Kinger Jun 10 '12 at 18:23
5

If your factory method returns the same object for equal inputs, it's safe to compare them with ==. For example String.intern works this way. Enums are also could be compared with ==. But Integer.valueOf returns the same object only for -128 ... 127 range (in default configuration).

Integer.valueOf(127) == Integer.valueOf(127)

but

Integer.valueOf(128) != Integer.valueOf(128)

Generally speaking you should use equals method to compare any objects. Operator == could be used to improve performance, when there are small number of different values for object. I wouldn't recommend to use this method, unless you are 100% sure in what you are doing.

vbezhenar
  • 11,148
  • 9
  • 49
  • 63
  • Thank you for understanding the question rather than stating the obvius. So my own immutable class, is it ok to use == instead of equals if I can assure no duplucates will ever exist. – Chetan Kinger Jun 10 '12 at 18:08
  • If your factory method works correctly and no one will do some dirty things like calling private constructors via reflection, everything should work. So if you have good reasons to do this, go ahead. – vbezhenar Jun 10 '12 at 18:17
2

Immutability and equality do not necessarily have something to do with each other. == compares for reference equality, that means, it compares if both variables point to the very same instance of the object. Equality means that both objects share the same value. Immutability now means that you can not alter an object after its construction.

So, you might have two immutable obejcts, that represents the same value (meaning, they are equals so that a.equals(b) returns true) but that are not the same instance.

I have a little example for you here:

public class MyPoint {
    private int x;
    private int y;

    public MyPoint(int x, int y) {
        this.x = x;
        this.y = y;
    }

    public int getX() {
        return x;
    }

    public int getY() {
        return y;
    }

    @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof MyPoint))
            return false;
        MyPoint p = (MyPoint) obj;
        return this.x == p.x && this.y == p.y;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        MyPoint p = new MyPoint(2, 2);
        MyPoint q = new MyPoint(2, 2);
        MyPoint r = q;

        System.out.println(p == q);
        System.out.println(p == r);
        System.out.println(q == r);
        System.out.println(p.equals(q));
        System.out.println(p.equals(r));
        System.out.println(q.equals(r));

    }

}

The output is: false false true true true true

MyPoint is immutable. You can not change its values / its state after is has been initialized. But, as you can see, two objects of myPoint might be equal, but they might not be the same instance.

I think what you have in mind is some kind of flyweight pattern, where only one object exists for every possible state of the object. Flyweight also means commonly that those obejcts are immutable.

Polygnome
  • 7,639
  • 2
  • 37
  • 57
1

They are not the same object, so == will not be true. With objects, be safe and use equals().

Mike Lentini
  • 1,359
  • 12
  • 15
  • 2
    Technically, `valueOf` caches (by default) -128 to 127 so in this case `a == b` will be true. But yes, definitely use `.equals` anyway. – Kevin Jun 10 '12 at 17:44
  • I am talking about a class that ensures no duplicates are created. Please try understanding a question before you jump to an answer.. – Chetan Kinger Jun 10 '12 at 18:12
  • 1
    @bot: Your original question did not state that the class ensured no duplicates were created. You edited your question after this answer was posted. – Mark Byers Jun 10 '12 at 18:25
  • I wanted a reconfirmation from the community. I wanted someone to tell me to ensure there are no duplicates. I thought my question was aching for this answer rather than the obvious equals vs == debate. – Chetan Kinger Jun 10 '12 at 18:40