Why can Java Collections not directly store Primitives types?

61,915

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.

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.

Share:
61,915
JavaUser
Author by

JavaUser

Updated on July 08, 2022

Comments

  • JavaUser
    JavaUser almost 2 years

    Java collections only store Objects, not primitive types; however we can store the wrapper classes.

    Why this constraint?

    • JPM
      JPM over 12 years
      This 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
      Shuba about 5 years
      Technically, primitive types are objects (singleton instances of such to be exact), they just aren't defined by a class, rather by the JVM. The statement int i = 1 defines a pointer to the singleton instance of the object which defines int in the JVM, set to the value 1 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 supertype Object - hence why A<?> compiles to A<Object> at run-time.
    • typeracer
      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
    Jeremy about 14 years
    Autoboxing was introduced in Java 1.5 along with Generics.
  • codenheim
    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
    DJClayworth over 13 years
    I 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
    DJClayworth over 13 years
    Not a mistake, a carefully chosen tradeoff which I believe has served Java very well indeed.
  • codenheim
    codenheim over 13 years
    It 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
    codenheim over 13 years
    But 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
    Jeremy over 13 years
    @mrjoltcola: What's your point? I was just sharing facts, not making an argument.
  • codenheim
    codenheim over 13 years
    My point is its important to point out the difference between syntax and performance. I also consider my comments fact-sharing, not argument. Thanks.
  • JAB
    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
    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
    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 type Object which identify boxed integers, but it should not be legal to pass an integer value]. Java's auto-unboxing is IMHO just nasty.