Java: Array of Primitive Data Types Does Not Autobox

Java: Array of primitive data types does not autobox

There is no autoboxing for arrays, only for primitives. I believe this is your problem.

Why autoboxing doesn't work in arrays?

Boxing converts an instance of a primitive type to an instance of the corresponding wrapper type.

It doesn't apply to array types.

Why?

  • Because that's how the language designers designed Java, and what the JLS specifies. The details are in JLS 5.1.7.

The JLS authors did not include an explanation for this decision. However, there are a number of reasons. Here are couple of the more obvious ones.

  • Efficiency. Converting an int[] to an Object[] entails visiting and converting all elements of the array. That is expensive (O(N)) ... and not something that should be hidden from the programmer behind some syntax.

  • Boxing an array would necessarily create a new array that is inherently different to the original one. You would be able to tell this in the following:

    int[] ints = new int[]{1,2,3};
    Integer[] arr = ints; // hypothetical boxing of the elements
    // What does ints.equals(arr) return?

    array[1] = 0;
    // What does ints[0] contain now?

    By contrast, (real) boxing and unboxing converts between values that are only distinguishable if you compare pointers ... and even then, not reliably.

The bottom line is that extending boxing and unboxing would introduce both efficiency and conceptual problems that would be hard to resolve.

Why is autoboxing not allowed for primitive arrays when using Arrays.sort()?

According to the JLS, Section 5.1.7, there are only the following specified boxing conversions, and arrays aren't involved in any of them:

Boxing conversion converts expressions of primitive type to
corresponding expressions of reference type. Specifically, the
following nine conversions are called the boxing conversions:

  • From type boolean to type Boolean

  • From type byte to type Byte

  • From type short to type Short

  • From type char to type Character

  • From type int to type Integer

  • From type long to type Long

  • From type float to type Float

  • From type double to type Double

  • From the null type to the null type

Why java does not autobox int[] to Integer[]

The difference is int[] is itself an Object, whereas Integer[] is an array of references to Integer object.

Arrays.asList(T...) method takes variable arguments of some type T with no upper bounds. The erasure of that method is Arrays.asList(Object...). That means it will take variable number of arguments of any type that extends from Object.

Since int is not an Object, but a primitive type, so it can't be passed as individual element of T[], whereas int[] is an Object itself, it will go as first element of the T[] array (T... internally is a T[] only). However, Integer[] will be passed as T[], with each reference in Integer[] passed as different argument to T[].

And even if you would argue that compiler should have done the conversion from each element of int[] array to Integer, well that would be too much work for the compiler. First it would need to take each array element, and box it to Integer, then it would need to internally create an Integer[] from those elements. That is really too much. It already has a direct conversion from int[] to Object, which it follows. Although I have always wished Java allowed implicit conversion from int[] to Integer[], that would have made life simpler while working with generics, but again, that's how the language is designed.

Take a simple example:

Object[] array = new Integer[10];  // this is valid conversion
Object[] array2 = new int[10]; // this is not
Object obj = new int[10]; // this is again a valid conversion

So, in your code Arrays.asList(intArray) returns a ArrayList<int[]> and not ArrayList<Integer>. You can't pass it to the ArrayList<Integer>() constructor.


Related:

  • int[] and Integer[]: What is the difference?

Why doesn't autoboxing work in this case with generics?

The code below seems to work as far as I can see:

public static <Integer extends Comparable<Integer>> int countGreaterThan(Integer [] anArray, Integer elem) {
int count = 0;
for (Integer e : anArray) {
if (e.compareTo(elem) > 0)
++count;
return count;
}

although I get a warning "The type parameter Integer is hiding the type Integer". See @ErichSchreiner's answer for an explanation of why auto-boxing won't work here.

A good generalisation of your method would be:

public static <T extends Comparable<? super T>> int countGreaterThan(T[] anArray, T elem) {
int count = 0;
for (T e : anArray) {
if (e.compareTo(elem) > 0) {
++count;
}
}
return count;
}

All you really care about is that anArray has the same type of elements as elem is, and that it is comparable to it's own type.

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?)

Primitive arrays in Java collections

ArrayList<int[]> will store arrays of primitives. There will be no autoboxing involved.

In Java, an array of any type -- primitive or not -- is an object and is therefore compatible with generics.

It is even possible to inadvertently end up with a container of int[], as illustrated by this fun question from yesterday: Java containsAll does not return true when given lists

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.



Related Topics



Leave a reply



Submit