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 anObject
) - Java does generics using type-erasure of reference types (e.g. a
List<?>
is really aList<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 anObject
, 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.
Solution 4
There is the concept of auto-boxing and auto-unboxing. If you attempt to store an int
in a List<Integer>
the Java compiler will automatically convert it to an Integer
.
Solution 5
Its not really a constraint is it?
Consider if you wanted to create a collection that stored primitive values. How would you write a collection that can store either int, or float or char? Most likely you will end up with multiple collections, so you will need an intlist and a charlist etc.
Taking advantage of the object oriented nature of Java when you write a collection class it can store any object so you need only one collection class. This idea, polymorphism, is very powerful and greatly simplifies the design of libraries.
JavaUser
Updated on July 08, 2022Comments
-
JavaUser almost 2 years
Java collections only store Objects, not primitive types; however we can store the wrapper classes.
Why this constraint?
-
JPM over 12 yearsThis constraint does suck when you deal with primitives and want to use Queues to send and your send rates are very fast. I am dealing with that issue right now of autoboxing taking too much time.
-
Shuba about 5 yearsTechnically, primitive types are objects (singleton instances of such to be exact), they just aren't defined by a
class
, rather by the JVM. The statementint i = 1
defines a pointer to the singleton instance of the object which definesint
in the JVM, set to the value1
defined somewhere in the JVM. Yes, pointers in Java - this is just abstracted away from you by the language implementation. Primitives can't be used as Generics because the language predicates all generic types must be of the supertypeObject
- hence whyA<?>
compiles toA<Object>
at run-time. -
typeracer over 4 years@RobertEFry primitive types are not objects in Java, so everything you wrote about singleton instances and such is fundamentally wrong and confusing. I suggest reading the "Types, Values, and Variables" chapter of the Java Language Specification, which defines what an object is: "An object (§4.3.1) is a dynamically created instance of a class type or a dynamically created array."
-
-
Jeremy about 14 yearsAutoboxing was introduced in Java 1.5 along with Generics.
-
codenheim about 14 years"How would you write a collection that can store either int, or float or char?" - With properly implemented generics / templates like other languages that do not pay the penalty of pretending everything is an object.
-
DJClayworth over 13 yearsI have hardly ever in six years of Java wanted to store a collection of primitives. Even in the few cases where I might have wanted to the extra time and space costs of using the reference objects has been negligible. In particular I find that many people think they want Map<int,T>, forgetting that an array will do that trick very nicely.
-
DJClayworth over 13 yearsNot a mistake, a carefully chosen tradeoff which I believe has served Java very well indeed.
-
codenheim over 13 yearsIt was enough of a mistake that .NET learned from it and implemented autoboxing from the start, and generics at the VM level without boxing overhead. Java's own attempt at a correction was only a syntax level solution that still suffers from the performance hit of autoboxing vs. no boxing at all. Java's implementation has shown poor performance with large data structures.
-
codenheim over 13 yearsBut its a compile time thing; syntax sugar without performance benefit. The Java compiler autoboxes, hence the performance penalty as compared to VM implementations like .NET who's generics don't involve boxing.
-
Jeremy over 13 years@mrjoltcola: What's your point? I was just sharing facts, not making an argument.
-
codenheim over 13 yearsMy point is its important to point out the difference between syntax and performance. I also consider my comments fact-sharing, not argument. Thanks.
-
JAB about 10 years@DJClayworth That only works well if the key values are non-sparse. Of course, you could use an auxiliary array to keep track of the keys, but that has its own issues: relatively efficient access would require keeping both arrays sorted based on the key order to allow binary searching, which in turn would make insertion and deletion inefficient unless the insertion/deletion is patterned such that inserted items are likely to end up where a previously deleted item was and/or some buffers are interspersed in the arrays, etc. There are resources available, but it'd be nice to have in Java itself.
-
DJClayworth about 10 years@JAB Actually it works fine if the keys are sparse, it just needs more memory than if they are not sparse. And if they keys are sparse, that implies there are not many of them and using Integer as a key works fine. Use whichever approach needs the least memory. Or whichever you feel like if you don't care.
-
supercat almost 10 years@mrjoltcola: IMHO, default autoboxing is a mistake, but there should have been a way to tag variables and parameters which should automatically box values given to them. Even now, I think there should be a means added to specify that certain variables or parameters should not accept newly-auto-boxed values [e.g. it should be legal to pass
Object.ReferenceEquals
references of typeObject
which identify boxed integers, but it should not be legal to pass an integer value]. Java's auto-unboxing is IMHO just nasty.