Why Can Java Collections Not Directly Store Primitives Types

Why can Java Collections not directly store Primitives types?

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.

Why primitive datatypes are not allowed in java.util.ArrayList?

All collection classes of java store memory location of the objects they collect. The primitive values do not fit in to the same definition.

To circumvent this problem, JDK5 and onwards have autoboxing - wherein the primitives are converted to appropriate objects and back when they are added or read from the collections. Refer to the official JDK tutorial on this topic.

Checking the JDK5 source code for ArrayList helps better understanding: creating an ArrayList<E> includes casting an Object[] array to E[].

Why it is possible to create collection of array of primitives but not collection of primitives

An object is a class instance or an array. The Java Language Specification mentions: In the Java programming language arrays are objects (§4.3.1), are dynamically created, and may be assigned to variables of type Object (§4.3.2). All methods of class Object may be invoked on an array.

https://docs.oracle.com/javase/specs/jls/se8/html/jls-10.html#:~:text=In%20the%20Java%20programming%20language,be%20invoked%20on%20an%20array.

Hence arrays can be used with Collections.

How can I store primitive types in Hashmap or list as a value instead of a wrapper class object

Using Java's Collection API, you cannot do it in a sensible way. Of course you could implement the List or Map interfaces yourself and decide to store primitives instead of objects but that would cause you a headache anyway. Java's collection interfaces are all based on objects (Generics don't even play a role in that), so you cannot have an add or remove method that takes an int as its argument.

Let's say you have your own implementation of List<Integer> that stores intinstead of the Integer defined by the interface, you could write something like this:

List<Integer> intList = new MyPrimitiveImplementation<>();
intList.add(42);

Now what happens is that the primitive int 42 gets autoboxed to an Integer object because the Collection interface defines the add method as add(Integer e). What your implementation could then do would be unboxing the Integer object again just to get the primitive back.

So, there's really no point. You either get serious performance trouble (imagine the above autoboxing happening a couple million times), or you lose compatibility with the Collections API. Both are undesirable.

Why can primitives not be stored in Java collections, but primitive arrays can?

Because Java arrays are objects, not primitives. And you can store references to objects in Java collections implemented as generic types.

From the Java language specification, Chapter 10: Arrays:

In the Java programming language, arrays are objects (§4.3.1), are dynamically created, and may be assigned to variables of type Object (§4.3.2). All methods of class Object may be invoked on an array.

Note that arrays and generics don't always play well together. Although you can create a collection of arrays, you can't create an array of collections. Type-checking of array contents is performed at run time. But the parameterized types of collections are not known at run time, because of type erasure. From Joshua Bloch's "Effective Java," 2nd ed., "Item 25: Prefer lists to arrays":

For example, it is illegal to create an array of generic type, a parameterized type, or a type parameter. None of these array creation expressions are legal: new List<E>[], new List<String>[], new E[]. All will result in generic array creation errors at compile time.

Why not auto-box Java primitive types for Generics?

So as far as I understand it, your proposed ArrayList<int> would be identical to ArrayList<Integer>. Is that right? (In other words, internally it still stores an Integer; and every time you put something in or get it out, it would automatically box/unbox it, but autoboxing/autounboxing already does that for ArrayList<Integer>.)

If it is the same, then I don't understand what the utility of having a duplicate syntax <int> is when it means the same thing as <Integer>. (In fact it will introduce additional problems, because for example int[] is not the same runtime type as Integer[], so if you have T[], and T is int, what would it mean?)

Java: Generic collection for holding primitives of different types

Primitives in Java can be boxed into objects. The type Object can hold values of type String, int, and boolean, by storing primitive int values as Integer objects, and primitive boolean values as Boolean objects. This can often be done implicity, without the programmer explicitly wrapping or unwrapping them, which is called auto-boxing.

Thus, any collection or array that holds values of type Object, such as Object[], List<Object>, etc., can hold your values.

For example:

Object[] array = new Object[] { 1, 2, true, "hi" };

List<Object> list = new ArrayList<Object>();
list.add(5);
list.add(false);
list.add("test");

For variable arguments to a method, just use Object...:

void myMethod(Object... params) {
// ...
}

When you process your array or collection of Object values, you can use instanceof and casts to find the types and handle them correctly.

Warning

When autoboxing to type Object, primitives are boxed into Object values directly. In the following code:

short shortValue = 0;
int myPrimitive = shortValue;
Object myObject = shortValue;

myPrimitive is of type int, but myObject is of type Short. You asked for an Object, and you got one! Be careful of what you assume about an object of type Object, because in principle you can't assume anything other than it is an Object.

Luckily, you don't always have to check an Object for every primitive number type. In most cases, it's enough to cast it to a Number, and then use for example .intValue() to get a value of type int. That is to say, code like this:

double x = 2.3;
Object o = x;
int i = ((Number) o).intValue();

is equivalent to this:

double x = 2.3;
int i = (int) x;


Related Topics



Leave a reply



Submit