Skip to main content

Boxing Conversion in J2SE 5.0

March 24, 2005

{cs.r.title}









Contents
The What and Why of Boxing Conversion
Boxing/Unboxing Conversions
   Boxing
   Unboxing
Method Invocation Conversion
Performance Issues
Immutable Objects
Ambiguous
Conclusion
Resources

Last year, Sun released its latest version of Java, J2SE 5.0, with lots of new
features. One of them is the autoboxing conversions for primitives and wrapper
objects. This article gives some insight about what boxing conversion in Java
is, and how it is used in real-world programming. I will present the meaning
of autoboxing and a few programming techniques.

The What and Why of Boxing Conversion

Boxing is a way to wrap primitive types with objects so that
they can be used like objects. For example, the Integer class
wraps the int primitive type in Java. Similarly, unboxing is
a way to convert object types back to primitive types.

From the programmer's perspective, reducing code size and improving performance
is an challenging job. The need to explicitly convert data from primitives
to object references arises frequently and is often burdensome.
Another annoying problem with using traditional casting techniques is that
it clutters up the code. J2SE 5.0's boxing/unboxing feature can eliminate these
problems.

Boxing/Unboxing Conversions

Let's look into the basic syntax of boxing/unboxing conversions:

Boxing

Consider the following code fragment:

   // Assigning primitive type to wrapper type
   Integer iWrapper = 10;
   

Prior to J2SE version 5.0, this code would not compile, since the Integer variable iWrapper expects
an object assignment, and 10 is an int primitive. It
will work if you modify the above code like this:

   // Assigning primitive type to wrapper type
   Integer iWrapper = new Integer(10);
   

This type of conversion occurs frequently throughout a program. It is annoying
to have to do this repeatedly. But if we use the J2SE 5.0 compiler, the conversion
will be done for us. This means the programmer can reduce the size of his or
her source code by avoiding unnecessary conversions. Of course, the Java Virtual
Machine (JVM) still does internal conversions back and forth between primitives
and objects, but at least the source is cleaner.

Unboxing

Unboxing is opposite of boxing: converting from object types to primitive
types. Consider the following code fragment:

   // Assigning object to primitive.
   public void intMethod(Integer iWrapper){
      int iPrimitive = iWrapper.intValue();
   }
   

In J2SE 1.4 and earlier, we are forced to perform explicit conversions.
It would be easier if there were no conversions at all. J2SE 5.0 gives you
the solution. Here's the modified version of the above code fragment:

   // Assigning object to primitive.
   public void intMethod(Integer iWrapper){
      int iPrimitive = iWrapper;
   }
 

It looks good, doesn't it? No more conversion with wrapper objects and primitive
objects!

Method Invocation Conversion

Autoboxing and unboxing can make method overloading interesting. Consider
the two overloaded methods shown here:

[prettify]
   public static void testMethod(long lVar){
      System.out.println("Long");
   }
   public static void test(Integer iVar){
      System.out.println("Integer");
   }

Here autoboxing plays a different role, choosing a most-specific method when   more than one method is applicable for method invocation.

If you call testMethod() with a primitive long parameter, then the first testMethod() is used. If you call testMethod() with an Integer object parameter, then the second testMethod() is used. There is nothing new there.

But what happens if you call testMethod() with an int parameter? In J2SE 1.4 and below, the int is promoted to a long and the first testMethod() is used. With autoboxing, it is acceptable that the int could be boxed into an Integer type and the second testMethod() used. That might even be what you want to happen--it might make more sense to convert an int to an Integer than to promote it to a long.

While arguably reasonable, that is not what happens. The general rule is, for compatibility reasons, the same behavior that applied in pre-5.0 versions must continue to hold. The reason is that existing code cannot suddenly start behaving differently when compiled and run under 5.0.

Consider the following code snippet:

   public void testMethod(Integer i){
      //statements
   }
   Public void testMethod(int i){
      //statements
   }

[/prettify]

When we invoke the method with an int as the parameter:

testMethod(10);

obviously, testMethod (int i) is called. Because when we invoke
a method, the compiler will first try to use normal method invocation, without
autoboxing. if it fails to find a matching method signature, then it will box
the values and try to find a matching method.

Consider the following code:

   Public void floatMethod(Float f){
      //statements
   }

What do you think will happen when we use the following invocation?

floatMethod(10);

Don't be surprised when the compiler rejects your code. Here, the widening
conversion has not taken place. The compiler searches for testMethod() with
an int argument. If it doesn't find one, it autoboxes and searches
for a testMethod() that takes an Integer argument. A programmer
might expect it to compile, expecting the compiler to employ widening conversions
to convert the int to a float and then autoboxing the float primitive to its
wrapper object. But that's not what the compiler does, and this confuses many
programmers.

Performance Issues

A few experiments have shown that autoboxing can be inefficient, and can
give you a false sense of efficiency. What may look like an efficient use of
primitive data types at the source-code level could well turn out to be a very
inefficient use of primitive data wrapper types when it comes to the runtime.

Look into the following code:

import java.util.*;
public class AutoBoxingPerformanceTest{
    public static void main(String args[]){
        long time1 = 0;
        long time2 = 0;
        List listValues = new ArrayList();
        int arrValues[] = new int[1000000];
        /* Inserting values into List and Array */
        for(int i =0;i<1000000;i++){
            listValues.add(i);
            arrValues[i]=i;
        }
        /* Reterive the values from collection objects and do the multiplication*/
        time1 = System.currentTimeMillis();
        for(int i=0;i<1000000;i++){
            listValues.set(i,listValues.get(i)*10);
        }
        time2 = System.currentTimeMillis();
        System.out.println("AutoBoxing with Collection : "+(time2-time1)+"ms");
        /* Reterive the values from arrays and do the multiplication*/
        time1 = System.currentTimeMillis();
        for(int i=0;i<1000000;i++){
            arrValues[i]=arrValues[i]*10;
        }
        time2 = System.currentTimeMillis();
        System.out.println("Using an Array : "+(time2-time1)+"ms");
    }
}

The output is:

AutoBoxing with Collection : 421ms
Using an Array : 0ms

When we are using the boxing conversions within a loop, it affects the performance
of a program. The above program is an example of how performance differs when
we use boxing conversion back and forth between primitives and wrappers from
a collection, versus normal arithmetic operation from an int array.

In this example, we first put a number of primitive values into an collection
and an array. Then we do an arithmetic operation on each value of the
collection or array. The array loop performs much better than the collection
loop, since collections need to perform boxing conversions before doing arithmetic
multiplication on their contents.

We have to be aware when we may be doing unnecessary things that could impact
performance, such as autoboxing when we should not.

The important point we must consider is that autoboxing doesn't reduce object
creation, but it reduces code complexity. A good rule of thumb is to use primitive
types where there is no need for objects, for two reasons:

  • Primitive types will not be slower than their corresponding wrapper types,
    and may be a lot faster.
  • There can be some unexpected behavior involving == (compare
    references) and .equals() (compare values).

Immutable Objects

Consider the following code fragment:

   Integer i1 = 100;
   Integer i2 = 100;
   Integer i3 = 1000;
   Integer i4 = 1000;
   System.out.println(i1==i2);
   System.out.println(i3==i4);

Can you guess what will be printed on the screen? If your answer is false--well, you're wrong.

In this case, J2SE 5.0 works differently. Certain ranges of values are stored
as immutable objects by the Java Virtual Machine. So, in this case, the
output is:

true
false

Normally, when the primitive types are boxed into the wrapper types, the JVM
allocates memory and creates a new object. But for some special cases, the
JVM reuses the same object.

The following is the list of primitives stored as immutable objects:

  • boolean values true and false
  • All byte values
  • short values between -128 and 127
  • int values between -128 and 127
  • char in the range \u0000 to \u007F

Ambiguous

While there is much to like about autoboxing, it does create a few problems.
Here are some scenarios where it will clutter your programming.

Consider the following program:

public class AutoBoxingTest{
    public static void main(String args[]){
        Integer iVar1 = new Integer(10);
        Integer iVar2 = 10;
        System.out.println("Lessthan Check : " + (iVar1 <= iVar2));
        System.out.println("Greater than Check : " + (iVar1 >= iVar2));
        System.out.println("Equality Check : " + (iVar1 == iVar2));
    }
}

The output is:

Less than Check : true
Greater than Check : true
Equality Check : false

When we are working with comparison operators, the boxing conversion works
a little differently. The output for the above program shows that, The operators <, <=, >, and >=
work fine. But when we come to the equals operator, ==, the JVM
interprets that as the operator for object equality, rather than unboxing ivar2 and
testing mathematical equality.

Conclusion

Autoboxing unclutters your code, but comes with important considerations
in terms of performance and sometimes unexpected behavior. This new feature
gives programmers more flexibility and can save lots of time. Hopefully, autoboxing
will reduce the more number of programming errors in the future by avoiding
the conversions back and forth between primitives and objects.

Resources

Krishna Srinivasan is a software engineer working in Chennai, India.
Related Topics >> Programming   |