Autoboxing is rather scary. While I fully understand the difference between ==
and .equals
I can\'t but help have the follow bug the hell out of m
If you skip autoboxing completely, you still get this behaviour.
final List<Integer> foo =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
final List<Integer> bar =
Arrays.asList(Integer.valueOf( 1 ), Integer.valueOf( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // true
System.out.println(foo.get(1) == bar.get(1)); // false
Be more explicit if you want a specific behavior:
final List<Integer> foo =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
final List<Integer> bar =
Arrays.asList( new Integer( 1 ), new Integer( 1000 ));
System.out.println(foo.get(0) == bar.get(0)); // false
System.out.println(foo.get(1) == bar.get(1)); // false
This is a reason, why Eclipse has autoboxing as a warning by default.
When you write
foo.get(0)
the compiler does not matter how you created the List. It only looks at the compile-time type of the List foo. So, if that is a List<Integer>, it will treat that as a List<Integer>, as it is supposed to do, and a List<Integer>'s get() always returns an Integer. If you want to use the == then you have to write
System.out.println(foo.get(0).intValue() == bar.get(0).intValue());
not
System.out.println(foo.get(0) == bar.get(0));
because that has a totally different meaning.
Can you imagine how bad performance would be if every Integer
carried overhead for internment? Also does not work for new Integer
.
The Java language (not a JVM issue) cannot always auto unbox because code designed for pre-1.5 Java should still work.
Integer
s in the byte range are the same object, because they are cached. Integer
s outside the byte range are not. If all integers were to be cached, imagine the memory required.
And from here
The result of all this magic is that you can largely ignore the distinction between int and Integer, with a few caveats. An Integer expression can have a null value. If your program tries to autounbox null, it will throw a NullPointerException. The == operator performs reference identity comparisons on Integer expressions and value equality comparisons on int expressions. Finally, there are performance costs associated with boxing and unboxing, even if it is done automatically
Every Integer between -128 and 127 is cached by java. They did this, supposedly, for the performance benefit. Even if they wanted to go back on this decision now, it's unlikely that they would. If anyone built code depending on this, their code would break when it was taken out. For hobby coding, this perhaps doesn't matter, but for enterprise code, people get upset and lawsuits happen.
All Integers cannot be cached, because the memory implications would be enormous.
Because the JVM cannot know what you wanted. Also, this change could easily break legacy code not built to handle this case.
If the JVM to automatically unboxed to primitives on calls to ==, this issue will actually become MORE confusing. Now you need to remember that == always compares object references, unless the Objects can be unboxed. This would cause yet more weird confusing cases just like the one you stated above.
Rather then worry too hard about this, just remember this rule instead:
NEVER compare objects with == unless you intend to be comparing them by their references. If you do that, I can't think of a scenario in which you'd run into an issue.
A lot of people have problems with this issue, even people that write books about Java.
In Pro Java Programming, mere inches below were the author talks about issues with using auto-boxed Integers as a key in an IdentityHashMap, he uses auto-boxed Integer keys in a WeakHashMap. The example values he uses are greater than 128, so his garbage collection call succeeds. If someone were to use his example and use values smaller than 128 though, his example would fail (due to the key being perma-cached).