10Mar
Comparing values of boxed primitives using equality operators in JAVA

Starting with version 5, JAVA support autoboxing and unboxing. Autoboxing is the automatic conversion that the Java compiler is making between primitive values and the corresponding instances of the wrapper classes: an int is converted to an Integer instance, a double to a Double instance and so on. The reverse conversion (from the object to the primitive value) is called unboxing. The compiler inserts specific calls to methods of the wrapper classes in order to make the conversions. More specifically:

it is calling static method valueOf that each wrapper class has (for example Integer.valueOf(1) converts primitive value 1 to an instance of Integer class holding the value 1.

it is calling the instance method corresponding to the primitive type to which is converting (for example, if it converts to int value, it calls Integer.intValue( )). All these instance methods are defines as abstract in the java.lang.Number parent class.

Let’s look at some examples:

Integer x = 1; //autoboxing
List list = new ArrayList( );
list.add( x );
list.add( 3 ); //autoboxing
if ( (x - 1) == 0 ) { //unboxing
System.out.println(“do something”);
}

It may come natural to say that comparing any boxed primitives will give the same results as performing the comparison on the actual primitive values. However is not always the case. The operators == and != are a particular case because they can be used both with primitive values and object references. All the other comparison operators (<, >, <=, >=) are used just for primitive values, so the compiler will force unboxing before doing any comparison.

Let’s look at the example below:

Integer x = 1000;
Integer y = 1000;
System.out.println( x == y); //it will print false
System.out.println(x.intValue() == y.intValue()); //it will print true

The reason the first System.out.println will print false to the console is very simple: although both x and y are wrapping the same primitive value, JAVA compiler, when performed autoboxing, has created 2 distinct instances of class Integer. As stated above, the equality operator == used with reference types will compare object references, in this case the two references are completely different.

But there are some exceptions. We take the same example but put 100 instead of 1000.

Integer x = 100;
Integer y = 100;
System.out.println( x == y); //it will print true
System.out.println(x.intValue() == y.intValue()); //it will print true

What is happening now? Why the first System.out.println statement is printing true? We only changed the value of the primitive. It turns out that the wrapper classes for primitive types performs some sort of caching but only for a subset of possible values corresponding to each primitive type. In the table below these subsets are specified for each primitive type.

The following primitive values are cached (or memoized, which is another common term used for this operation) during autoboxing:

Boolean: true, false

Byte: the complete range (0-255)

Character: char values in range from \u0000 to \u007F,

Integer/Short: values between -128 and 127.

One can easily notice that the values cached are those which can be represented on a single byte of memory.

Looking back to our example, this means that Integer class holds a cache with values between -128 and 127 and every time there is a instantiation of a new Integer object with a primitive value within that range, it will return that one instance from the cache, always, and will not create a second one. That is why, in the second example x and y actually holds the same object reference and that is why the equality operator has returned true.

How about this situation:

Integer x = 1000;
Integer y = 1000;
System.out.println( x == y); //it will print true
System.out.println(x.intValue() == y); //it will print true

Why now the first System.out.println prints true? We made a small but crucial change: y is a primitive value. In this case, the JAVA compiler will force unboxing for variable x before evaluating the equality since the operands are of different types: one is of reference type and the other of primitive type. After unboxing, the equality operator is applied to 2 primitive values, so it works as expected.

You must be very careful with this corner cases since they can introduce a lot of problems in your code, and it is very hard to track them down. It is recommended to avoid relying on equality operators applied to reference types and always them back to the primitive value explicitly, and then perform the equality comparison on those values. Unless you are absolutely sure about the fact that the values will always be in the range of those cached, so use directly the equality operator, which will be a performance improvement.

Integer x = 100;
Integer y = 100;
System.out.println( x == y); //it will print true
System.out.println(x.intValue() == y.intValue()); //it will print true