Thursday, November 29, 2007

Java 1.5 Autoboxing

I know I am well behind the times with Java 1.5 features (I have only been working with 1.5 for about a month now), but out of all of the new features, I think autoboxing is hands down the best. Generics are good, sure, but really verbose and casting is nowhere near as painful as wrapping primitives.

For those of you that don't know, Autoboxing automatically boxes and unboxes java primitive values into there corresponding types. So for example, to put key values in a map by integers prior to Autoboxing you would have to do as follows.

map.put( new Integer( 1 ), "Value" );

Where with Autoboxing you can just do

map.put( 1, "Value" );

At runtime, java will automatically wrap (or box) the integer for you so that it can be treated as an Object.


Java will do this in any scenario with any primitive. For example.

Boolean b = true; // instead of Boolean b = new Boolean( true )
Integer i = 1; // instead of Integer i = new Integer( 1 )
int i2 = i; // instead of int i2 = i.intValue()
Apologies to those of your that have been Autoboxing for years...

Performance
So in a pre 1.5 codebase that is now being built with 1.5, I make a habit of changing code to take advantage of this wherever I go. However, I had heard about performance issues with autoboxing, and you always get those developers who like to make bold statements about performance issues without any empirical evidence, so I first decided to dig into the performance implications. I wrote some tests that put primitives into collections with and without Autoboxing (just like in the first example above). To my initial surprise, each method took roughly the same amount of time and the Autoboxing was slightly faster!

Oivey! (I am not jewish, just thought it would be fun to use that phrase). In both tests the value was being boxed, the only difference was that I didn't have to explicitly do it myself. Of course the performance was comparable. This clearly wasn't a performance issue. Go forth and change manual boxing to autobox!

The potential performance problem with Autoboxing is when you autobox a value if you didn't need to treat it as a Object, and you could have done just as well with the primitive. In this case you are making the computer do needless work (the mechanics or the autobox) but potentially more importantly you are using up more memory because an Integer has a bigger footprint than a int (and also potentially giving the garbage collector more to do).

However, there is another twist. Autoboxing an int to an Integer can offer a performance gain, because Integer's are immutable and therefore can be cached and reused by the VM.

Here is a good article on the subject from Dr Dobbs.

4 comments:

Frederick Polgardy said...

Just beware:

Integer a1 = new Integer(100);
Integer a2 = new Integer(100);
a1 == a2 // --> false!

It's tempting to use autoboxed numbers naively just as you would use the corresponding primitives; but they're not a drop-in replacement. You still have to know whether you're dealing with primitives or objects, and to use comparison operators properly.

What does this do?

int a1 = 100;
Integer a2 = new Integer(100);
a1 == a2 // --> ?

Does a1 get boxed to perform an Integer/Integer comparison, or does a2 get unboxed to perform an int/int comparison? Better do it explicitly, just to make sure.

reevesy said...

All good points. Thanks Fred.

Andy Maleh said...

Thanks for the post Gareth.

My favorite Java 1.5 feature is definitely Generics. Advanced uses of them work like a wizard's wand on the code. For certain situations, you can even utilize Generics to infer types automatically through the Class<T> trick. I think I will blog about that someday as I applied it in my current project after learning it from another programmer while pairing.

I ran comprisons on the following pairs of Integers:

Integer n1 = new Integer(1);
Integer n2 = new Integer(1);

Integer n3 = 1;
Integer n4 = 1;

Integer n5 = 1;
Integer n6 = Integer.valueOf(1);

Here is what I got:

n1 == n2: false

n3 == n4: true

n5 == n6: true

As you can see, n6 utilizes the Integer.valueOf method. Here is what the Java API documentation says about it:

Returns a Integer instance representing the specified int value. If a new Integer instance is not required, this method should generally be used in preference to the constructor Integer(int), as this method is likely to yield significantly better space and time performance by caching frequently requested values.

So, to address the concerns that Fred raised, use Integer.valueOf instead of the Integer constructor unless you intetionally want a non-equal Integer object. In fact, valueOf seems to be the mechanism that auto-boxing relies on behind the scenes as demonstrated by the last (n5 and n6) pair comparison.

Frederick Polgardy said...

Type inference in generics is pretty cool: it's actually the whole point behind the rather bizarre situation that java.lang.Class is now a parameterized type such that T.class <=> Class<T>. I grokked type inference pretty quickly since I've been working with C++ templates for years, of which Java generics are a pale shadow. They're really just syntactic sugar, but in this case, the syntactic sugar is pretty sweet.

Yes, Integer.valueOf() is the preferred way of manually "boxing" an int in Java 5+, because it takes advantage of the fact that the JVM now caches the boxed versions of true and false, as well as a bunch of chars and ints. But don't depend on it to do what you're doing: replace the 1's in your example by 101's for a fun surprise! (Integers for 0-99 are cached.)

Use valueOf to box ints, but don't use it to make comparisons "correct"; you still need to use equals() for objects and == for primitives.