How can I properly compare two Integers in Java?
Asked Answered
D

10

305

I know that if you compare a boxed primitive Integer with a constant such as:

Integer a = 4;
if (a < 5)

a will automatically be unboxed and the comparison will work.

However, what happens when you are comparing two boxed Integers and want to compare either equality or less than/greater than?

Integer a = 4;
Integer b = 5;

if (a == b)

Will the above code result in checking to see if they are the same object, or will it auto-unbox in that case?

What about:

Integer a = 4;
Integer b = 5;

if (a < b)

?

Disestablish answered 3/10, 2009 at 21:30 Comment(3)
@Bart Kiers: An explicit experiment could only disprove, not prove that unboxing occurs. If using == instead of equals yields the correct result, that may be because the boxed numbers are being interned or otherwise reused (as a compiler optimization, presumably). The reason to ask this question is to find out what's happening internally, not what appears to be happening. (At least, that's why I'm here.)Mount
Rant. By far the stupidest thing about Java is the inability to override operators, such as == and < to do something sensible, for example, with String and Integer types. Therefore you have to use a.equals(b) or b.equals(a) instead. And if you want to handle null (as you ought!) you have to use Objects.equals(a,b).Wilford
I tried Integer a = 4 Integer b = 4; a==b retruns false. I had to use if (x.intValue() == y.intValue())Tessi
B
406

No, == between Integer, Long etc will check for reference equality - i.e.

Integer x = ...;
Integer y = ...;

System.out.println(x == y);

this will check whether x and y refer to the same object rather than equal objects.

So

Integer x = new Integer(10);
Integer y = new Integer(10);

System.out.println(x == y);

is guaranteed to print false. Interning of "small" autoboxed values can lead to tricky results:

Integer x = 10;
Integer y = 10;

System.out.println(x == y);

This will print true, due to the rules of boxing (JLS section 5.1.7). It's still reference equality being used, but the references genuinely are equal.

If the value p being boxed is an integer literal of type int between -128 and 127 inclusive (§3.10.1), or the boolean literal true or false (§3.10.3), or a character literal between '\u0000' and '\u007f' inclusive (§3.10.4), then let a and b be the results of any two boxing conversions of p. It is always the case that a == b.

Personally I'd use:

if (x.intValue() == y.intValue())

or

if (x.equals(y))

As you say, for any comparison between a wrapper type (Integer, Long etc) and a numeric type (int, long etc) the wrapper type value is unboxed and the test is applied to the primitive values involved.

This occurs as part of binary numeric promotion (JLS section 5.6.2). Look at each individual operator's documentation to see whether it's applied. For example, from the docs for == and != (JLS 15.21.1):

If the operands of an equality operator are both of numeric type, or one is of numeric type and the other is convertible (§5.1.8) to numeric type, binary numeric promotion is performed on the operands (§5.6.2).

and for <, <=, > and >= (JLS 15.20.1)

The type of each of the operands of a numerical comparison operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs. Binary numeric promotion is performed on the operands (§5.6.2). If the promoted type of the operands is int or long, then signed integer comparison is performed; if this promoted type is float or double, then floating-point comparison is performed.

Note how none of this is considered as part of the situation where neither type is a numeric type.

Bellyband answered 4/10, 2009 at 7:10 Comment(7)
Is there any reason why one would want to write x.compareTo(y) < 0 instead of x < y?Suanne
@MaxNanasy: Not that I can immediately think of.Bellyband
As of Java 1.6.27+ there an overload on equals in the Integer class, so it should be as efficient as calling .intValue(). It compares the values as primitive int.Irreproachable
As @Irreproachable said, this is not necessary in Java 8 anymore. The comparison of Integer with Integer is by value by default.Exile
@Axel: The addition of an overload wouldn't change the behaviour of the == operator though, would it? I'm not in a position to test right now, but I'd be very surprised if that had changed.Bellyband
@JonSkeet It's about The latter is slightly less efficient - there isn't an overload for Integer.equals(Integer).Beelzebub
Damn, this -128 and 127 always gets me...Cagliostro
C
50

Since Java 1.7 you can use Objects.equals:

java.util.Objects.equals(oneInteger, anotherInteger);

Returns true if the arguments are equal to each other and false otherwise. Consequently, if both arguments are null, true is returned and if exactly one argument is null, false is returned. Otherwise, equality is determined by using the equals method of the first argument.

Countermarch answered 22/3, 2018 at 14:57 Comment(2)
This handles nulls so makes it simple. Thanks!Salado
public static boolean equals(Object a, Object b) { return (a == b) || (a != null && a.equals(b));//This handle null }Russell
A
49

== will still test object equality. It is easy to be fooled, however:

Integer a = 10;
Integer b = 10;

System.out.println(a == b); //prints true

Integer c = new Integer(10);
Integer d = new Integer(10);

System.out.println(c == d); //prints false

Your examples with inequalities will work since they are not defined on Objects. However, with the == comparison, object equality will still be checked. In this case, when you initialize the objects from a boxed primitive, the same object is used (for both a and b). This is an okay optimization since the primitive box classes are immutable.

Arithmetician answered 3/10, 2009 at 21:39 Comment(11)
I figured it was object equality being tested. I had some weird results. Should I replace it with .equals()? Also, do you feel I should leave the inequalities as they are or do it another way as well?Disestablish
There are some non-obvious edge cases with autoboxing. I have my IDE (Eclipse) set to color anything being un-boxed in red, this has saved me from bugs on a few occasions. If you are comparing two Integers, use .equals, if you want to make your inequalities clear, write the cast in explicitly: if ((int)c < (int)d) ... ; You can also do: c.compareTo(d) < 0 // === c < dArithmetician
And if you change the number literals to 200, both tests will print false.Intradermal
... on most JVM implementations, that is. According to the language spec the result could vary between implementations.Intradermal
I think it's clearer to call this "reference equality" - that way it's obvious what you mean. I'd normally understand "object equality" to mean "the result of equals being called".Bellyband
+1 Jon :-) I was just thinking 'Isn't (c == d) comparing the object reference variable?'Tillett
But Integer a = 3000; Integer b = 3000; System.out.println(a == b); //prints falseMarmara
@AdamLewis Could you please send me a link that explains what to do, in order to get the coloring working as you did? :) Many Thanks in advance!Tannatannage
For everyone else looking how to get the coloring working: in Eclipse hit Ctrl+3 and search for Syntax coloring.Impressment
I think this is clearly not an OK optimization, since the it only works for small autoboxed primitives, and what set of autoboxed primitives it works for will vary from one implementation to another, making your code fragile.Mauricemauricio
Watch out, Integer c = Integer.valueOf(10); Integer d = Integer.valueOf(10); System.out.println(c == d); //prints TRUE Because valueOf uses a cach from -127 to +127. As for Long.valueOf. So don't rely on == except for Primitive or EnumDisposition
R
25

We should always go for the equals() method for comparison of two integers. It's the recommended practice.

If we compare two integers using == that would work for certain range of integer values (Integer from -128 to 127) due to the JVM's internal optimisation.

Please see examples:

Case 1:

Integer a = 100;
Integer b = 100;

if (a == b) {
  System.out.println("a and b are equal");
} else {
  System.out.println("a and b are not equal");
}

In above case JVM uses value of a and b from cached pool and return the same object instance(therefore memory address) of integer object and we get both are equal.Its an optimisation JVM does for certain range values.

Case 2: In this case, a and b are not equal because it does not come with the range from -128 to 127.

Integer a = 220;
Integer b = 220;
   
if (a == b) {
  System.out.println("a and b are equal");
} else {
  System.out.println("a and b are not equal");
}

Proper way:

Integer a = 200;             
Integer b = 200;  
System.out.println("a == b? " + a.equals(b)); // true
Replete answered 4/12, 2019 at 12:51 Comment(2)
Thank you :) My code runs 2 years until now.... Database indexes grows and happen that some were > 128 and database update stops work. This was the case...Pungy
"... due to the JVM's internal optimisation" - This seems to imply that this is some magic going on under the hood in the JVM. In fact, that behavior for that range of Integer values is mandated by the Java Language Specification; see the JLS citation in Jon Skeet's answer.Randle
D
11

== checks for reference equality, however when writing code like:

Integer a = 1;
Integer b = 1;

Java is smart enough to reuse the same immutable for a and b, so this is true: a == b. Curious, I wrote a small example to show where java stops optimizing in this way:

public class BoxingLol {
    public static void main(String[] args) {
        for (int i = 0; i < Integer.MAX_VALUE; i++) {
            Integer a = i;
            Integer b = i;
            if (a != b) {
                System.out.println("Done: " + i);
                System.exit(0);
            }
        }
        System.out.println("Done, all values equal");
    }
}

When I compile and run this (on my machine), I get:

Done: 128
Dialyser answered 15/7, 2015 at 21:30 Comment(8)
tl;dr -1 for handwaving ; #15052716 #20897520 https://mcmap.net/q/48639/-integers-caching-in-java-duplicate etc explain in-detail the matter you mentioned; it's better to read the docs (or lib source) than to create pseudo-tests with the risk of high locality of the results - not only have you completely forgotten about the lower bound of the cache (i.e. -128 by default), not only you have off-by-one (the max is 127, not 128),Luisluisa
but you have completely no guarantee to receive the same result on any machine - since you can easily increase the cache size yourself, YMMV. Also, OP's question was how to properly compare two Integers - you haven't answered it at all.Luisluisa
I respect your opinion and perception here. I think we just have fundamentally different approaches to CS.Dialyser
it's not about opinion nor perception - it's about the facts, which you sincerelly missed. Doing a pseudo-test proving nothing, without any hard backing data (docs, source etc.) and without answering OP's questions doesn't deserve to be called neither good Q&A nor CS. As to "different approach" - CS is, by definition, a science; what you did science is not; it's a misleading trivia (or it would be an intriguing comment, if stated properly) - if you wish it to be science, correct the fundamental flaws in your answer or debunk them sensibly, as that's how peer review works.Luisluisa
Sure then, I'll try to address the flaws. I didn't forget about the lower bound, I didn't feel it was interesting and chose not to include it. I don't believe I have an off by one error, I stated the way that java (which I clarified on my machine, in my situation) stopped optimizing this was, which is at 128. If I had stated the max value it did this for, than you're right the answer would have been 127.Dialyser
To answer your larger question, I approach learning about programming and teaching it not by reading docs, but by poking at the world (tools, languages, frameworks, etc) around us and observing the results. However, you make a good point that this is really more of a comment. Given that SO doesn't have a way for me to make this longer exploratory comment including code, I'm going to leave it up as an answer for now. If you vote to close, I wouldn't be offended and would let the votes/system handle it.Dialyser
when writing an answer in Q/A format, your answer musn't be about what you feel personally interesting, but about what OP (and thus, the question itself) needs/requires and/or what a random visitor to that given Q/A would consider either a very useful, clear and verifiable information regarding the question - again, your code is misleading to future visitors (for the reasons outlined in my first comment), and the approach you present can be a source of many bad programming habits. as to why -Luisluisa
(and regarding the "learning approaches"), bear the fact that "poking the world" has any sense only when it's done systematically, in a way that actually proves something. That's how good benchmarks work, that's how reverse engineering works, that's how science works. You haven't done that (note it's already done in the Q/As I've linked in the beginning), since you presented the data (code & output) without a) backing data (docs), b) extensive test coverage. I honestly prefer improving over flagging & removing; also, there ain't no "close" for answers - it's for questions only AFAIK.Luisluisa
L
10

tl;dr my opinion is to use a unary + to trigger the unboxing on one of the operands when checking for value equality, and simply use the maths operators otherwise. Rationale follows:

It has been mentioned already that == comparison for Integer is identity comparison, which is usually not what a programmer want, and that the aim is to do value comparison; still, I've done a little science about how to do that comparison most efficiently, both in term of code compactness, correctness and speed.

I used the usual bunch of methods:

public boolean method1() {
    Integer i1 = 7, i2 = 5;
    return i1.equals( i2 );
}

public boolean method2() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2.intValue();
}

public boolean method3() {
    Integer i1 = 7, i2 = 5;
    return i1.intValue() == i2;
}

public boolean method4() {
    Integer i1 = 7, i2 = 5;
    return i1 == +i2;
}

public boolean method5() { // obviously not what we want..
    Integer i1 = 7, i2 = 5;
    return i1 == i2;
}

and got this code after compilation and decompilation:

public boolean method1() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    return var1.equals( var2 );
}

public boolean method2() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method3() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method4() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2.intValue() == var1.intValue() ) {
        return true;
    } else {
        return false;
    }
}

public boolean method5() {
    Integer var1 = Integer.valueOf( 7 );
    Integer var2 = Integer.valueOf( 5 );

    if ( var2 == var1 ) {
        return true;
    } else {
        return false;
    }
}

As you can easily see, method 1 calls Integer.equals() (obviously), methods 2-4 result in exactly the same code, unwrapping the values by means of .intValue() and then comparing them directly, and method 5 just triggers an identity comparison, being the incorrect way to compare values.

Since (as already mentioned by e.g. JS) equals() incurs an overhead (it has to do instanceof and an unchecked cast), methods 2-4 will work with exactly the same speed, noticingly better than method 1 when used in tight loops, since HotSpot is not likely to optimize out the casts & instanceof.

It's quite similar with other comparison operators (e.g. </>) - they will trigger unboxing, while using compareTo() won't - but this time, the operation is highly optimizable by HS since intValue() is just a getter method (prime candidate to being optimized out).

In my opinion, the seldom used version 4 is the most concise way - every seasoned C/Java developer knows that unary plus is in most cases equal to cast to int/.intValue() - while it may be a little WTF moment for some (mostly those who didn't use unary plus in their lifetime), it arguably shows the intent most clearly and most tersely - it shows that we want an int value of one of the operands, forcing the other value to unbox as well. It is also unarguably most similar to the regular i1 == i2 comparison used for primitive int values.

My vote goes for i1 == +i2 & i1 > i2 style for Integer objects, both for performance & consistency reasons. It also makes the code portable to primitives without changing anything other than the type declaration. Using named methods seems like introducing semantic noise to me, similar to the much-criticized bigInt.add(10).multiply(-3) style.

Luisluisa answered 23/5, 2015 at 16:52 Comment(3)
Can you explain what the + means in method 4? I tried to google it but I only got the normal usages of that symbol (addition, concatenation).Kuomintang
@AlexLi it means exactly what I wrote - unary + (unary plus), see e.g. #2624910Luisluisa
Concerning the overhead of the equals call, the JIT compiler should inline the code of this call, and then peephole optimization should result in native code that is equivalent to that generated for the unary + trick.Randle
I
8

Calling

if (a == b)

Will work most of the time, but it's not guaranteed to always work, so do not use it.

The most proper way to compare two Integer classes for equality, assuming they are named 'a' and 'b' is to call:

if(a != null && a.equals(b)) {
  System.out.println("They are equal");
}

You can also use this way which is slightly faster.

   if(a != null && b != null && (a.intValue() == b.intValue())) {
      System.out.println("They are equal");
    } 

On my machine 99 billion operations took 47 seconds using the first method, and 46 seconds using the second method. You would need to be comparing billions of values to see any difference.

Note that 'a' may be null since it's an Object. Comparing in this way will not cause a null pointer exception.

For comparing greater and less than, use

if (a != null && b!=null) {
    int compareValue = a.compareTo(b);
    if (compareValue > 0) {
        System.out.println("a is greater than b");
    } else if (compareValue < 0) {
        System.out.println("b is greater than a");
    } else {
            System.out.println("a and b are equal");
    }
} else {
    System.out.println("a or b is null, cannot compare");
}
Irreproachable answered 22/9, 2016 at 17:55 Comment(3)
if (a==b) works only for small values and will not work most of the times.Freckle
It works up to 127 as that is Java's default Integer cache, which makes sure all numbers up to 127 have the same reference value. You can set the cache to go higher than 127 if you'd like, but just don't use == to be safe.Irreproachable
Yeah "Will work most of the time" is extremely generous... you should really reword thatNazareth
N
1

In my case I had to compare two Integers for equality where both of them could be null. I searched similar topics, but I didn't find anything elegant for this. I came up with simple utility function:

public static boolean integersEqual(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return true;
    }
    if (i1 == null && i2 != null) {
        return false;
    }
    if (i1 != null && i2 == null) {
        return false;
    }
    return i1.intValue() == i2.intValue();
}

// Considering null is less than not-null
public static int integersCompare(Integer i1, Integer i2) {
    if (i1 == null && i2 == null) {
        return 0;
    }
    if (i1 == null && i2 != null) {
        return -1;
    }
    return i1.compareTo(i2);
}
Nourishment answered 21/9, 2018 at 1:14 Comment(0)
Q
-1

Because a comparison method has to be done based on type int (x==y) or class Integer (x.equals(y)) with the right operator:

public class Example {

    public static void main(String[] args) {
        int[] arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<arr.length-1; j++)
            if((arr[j-1] != arr[j]) && (arr[j] != arr[j+1]))
                System.out.println("int>" + arr[j]);

        Integer[] I_arr = {-32735, -32735, -32700, -32645, -32645, -32560, -32560};

        for(int j=1; j<I_arr.length-1; j++)
            if((!I_arr[j-1].equals(I_arr[j])) && (!I_arr[j].equals(I_arr[j+1])))
                System.out.println("Interger>" + I_arr[j]);
    }
}
Quorum answered 3/10, 2019 at 9:37 Comment(0)
S
-2

This method compares two Integer's with a null check. See the tests.

public static boolean compare(Integer int1, Integer int2) {
    if(int1!=null) {
        return int1.equals(int2);
    } else {
        return int2==null;
    }
    //inline version:
       //return (int1!=null) ? int1.equals(int2) : int2==null;
}

//results:
System.out.println(compare(1,1));           //true
System.out.println(compare(0,1));           //false
System.out.println(compare(1,0));           //false
System.out.println(compare(null,0));        //false
System.out.println(compare(0,null));        //false
System.out.println(compare(null,null));        //true
Sokotra answered 2/3, 2017 at 12:6 Comment(1)
For this, I think it'd just be better to use Objects.equals(x,y) method instead of rolling your own.Eyelet

© 2022 - 2024 — McMap. All rights reserved.