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 anObject[]
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[]
andInteger[]
: 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
How to Manage Exceptions Thrown in Filters in Spring
How to Build Sources Jar with Gradle
What Does the "Assert" Keyword Do
How to Create a Sub Array from Another Array in Java
Spring Cache @Cacheable - Not Working While Calling from Another Method of the Same Bean
Generic Type Parameter Naming Convention for Java (With Multiple Chars)
Timer & Timertask Versus Thread + Sleep in Java
Why Can't I Declare Static Methods in an Interface
Synchronization of Non-Final Field
Spring Boot Multiple Datasource
How to Change an Eclipse Default Project into a Java Project
Java Arraylist - How to Tell If Two Lists Are Equal, Order Not Mattering
Differencebetween Closeablehttpclient and Httpclient in Apache Httpclient API
Barcode Scanner Implementation on Java
How to Solve "Java.Io.Ioexception: Error=12, Cannot Allocate Memory" Calling Runtime#Exec()