Generics mostly. You can have a Map<Integer, Object> but it's impossible to do it with an int.
Integers can be a PITA in some cases though, for example, run this code:
Integer a = 20;
Integer b = Integer.valueOf(20);
System.out.println(a == b); //prints "true"
then try this:
a = 400;
b = Integer.valueOf(400);
System.out.println(a == b); //prints "false"
wtf? Integer caching is the culprit. Integer class keeps a cache of constants from -128 to 127, so calling valueOf on a number within that range and assigning such value directly will return the same Integer object. Anything above this though, and you're back to the object vs. reference equality land again.
Confused yet? How about this:
a = new Integer(25);
b = new Integer(25);
System.out.println(a == b);
What do you think it will print? (hint - it's false) :]
But wait there's moar!
Are you familiar with ternary operators? it's basically a syntactic sugar for if...else:
int result = a > 20 ? a : 20
//is the same as
if (a > 20) result = a;
else result = 20;
Now let's try this:
int num = 0;
int result = (num == 0 ? null : 100);
Oops, you crash with a NullPointerException.
But in this case:
int num = 0;
Integer result = (num == 0 ? null : 100);
Java will happily print null like it's supposed to. Because, again, to assign to a primitive you need to unbox the result of the ternary expression - meaning to turn an Integer to an int Java calls Integer.intVaue() on it, and calling method on a null object resulst in an NPE. However it's safe to assign a null to the reference variable (Integer).