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:
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
}
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++){>
}
/* Reterive the values from collection objects and do the multiplication*/
time1 = System.currentTimeMillis();
for(int i=0;i<1000000;i++) time2 =" System.currentTimeMillis();" time1 =" System.currentTimeMillis();" 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).
Resources
Sun Microsystems' Autoboxing Tutorial
Specification for Autoboxing
Autoboxing Surprises from J2SE 5
No comments:
Post a Comment