Why can Java Collections not directly store Primitives types?

Solution 1:

It was a Java design decision, and one that some consider a mistake. Containers want Objects and primitives don't derive from Object.

This is one place that .NET designers learned from the JVM and implemented value types and generics such that boxing is eliminated in many cases. In CLR, generic containers can store value types as part of the underlying container structure.

Java opted to add generic support 100% in the compiler without support from the JVM. The JVM being what it is, doesn't support a "non-object" object. Java generics allow you to pretend there is no wrapper, but you still pay the performance price of boxing. This is IMPORTANT for certain classes of programs.

Boxing is a technical compromise, and I feel it is implementation detail leaking into the language. Autoboxing is nice syntactic sugar, but is still a performance penalty. If anything, I'd like the compiler to warn me when it autoboxes. (For all I know, it may now, I wrote this answer in 2010).

A good explanation on SO about boxing: Why do some languages need Boxing and Unboxing?

And criticism of Java generics: Why do some claim that Java's implementation of generics is bad?

In Java's defense, it is easy to look backwards and criticize. The JVM has withstood the test of time, and is a good design in many respects.

Solution 2:

Makes the implementation easier. Since Java primitives are not considered Objects, you would need to create a separate collection class for each of these primitives (no template code to share).

You can do that, of course, just see GNU Trove, Apache Commons Primitives or HPPC.

Unless you have really large collections, the overhead for the wrappers does not matter enough for people to care (and when you do have really large primitive collections, you might want to spend the effort to look at using/building a specialized data structure for them).

Solution 3:

It's a combination of two facts:

  • Java primitive types are not reference types (e.g. an int is not an Object)
  • Java does generics using type-erasure of reference types (e.g. a List<?> is really a List<Object> at run-time)

Since both of these are true, generic Java collections can not store primitive types directly. For convenience, autoboxing is introduced to allow primitive types to be automatically boxed as reference types. Make no mistake about it, though, the collections are still storing object references regardless.

Could this have been avoided? Perhaps.

  • If an int is an Object, then there's no need for box types at all.
  • If generics aren't done using type-erasure, then primitives could've been used for type parameters.