I was messing around in CodeFigths and I got to a situation like this:
int makeArrayConsecutive2(int[] statues) {
Set<Integer> statueSet = new TreeSet<>(Arrays.asList(statues));
for (int statue : statueSet) {
System.out.println(statue);
}
}
Logically, I have noticed that the compiler is doing a lot of Autoboxing and Unboxing, so the question has arisen:
Should I force unbox a collection of Wrappers, or use the Wrapper directly? What are the advantages and disadvantages of each option?
Let's go in parts.
First , the code you present does not compile . The problem is that you can't infer the data type from here:
Actually, the inferred data type is
Integer
nothing butint[]
. It happens that the signature of the method isArrays#asList(Object ... varargs)
.int[]
is a class , so by having the following:To make your code compile, you either change the data type of the parameter from
int[]
toInteger[]
or change theSet<Integer>
toSet<int[]>
. I recommend changing the data type of the parameterInteger[]
.Second , the autoboxing in your code is executed only in one part:
The improved for, which has been included since Java 6, is syntax sugar for the following (except for arrays of primitives):
In this case, since you force the iteration to be done with a primitive, the unboxing will be done at that moment only:
Now, what is the performance cost of boxing/unboxing?
I did some tests using Java 1.8.0_65, JMH 1.19 (because the tests using it
System.nanoTime()
are very innocent) on MacOS X Sierra, 2.7 GHz Intel Core i5 processor, 8 GBs of ram, 256 MBs dedicated for the JVM. These are the results:Loop through the elements of an array of 10 elements, without unboxing:
0.001 ±(99.9%) 0.001 ms/op [Average]
(min, avg, max) = (≈ 10⁻³, 0.001, 0.001), stdev = 0.001
CI (99.9%): [0.001, 0.001] (assumes normal distribution )
Loop through the elements of an array of 10 elements, with unboxing:
0.001 ±(99.9%) 0.001 ms/op [Average]
(min, avg, max) = (≈ 10⁻³, 0.001, 0.001), stdev = 0.001
CI (99.9%): [≈ 10⁻³, 0.001] ( assumes normal distribution)
Loop through the elements of an array of 10,000 elements, without unboxing:
0.001 ±(99.9%) 0.001 ms/op [Average]
(min, avg, max) = (≈ 10⁻³, 0.001, 0.001), stdev = 0.001
CI (99.9%): [≈ 10⁻³, 0.001] ( assumes normal distribution)
Loop through the elements of an array of 10,000 elements, with unboxing:
0.001 ±(99.9%) 0.001 ms/op [Average]
(min, avg, max) = (≈ 10⁻³, 0.001, 0.001), stdev = 0.001
CI (99.9%): [≈ 10⁻³, 0.001] ( assumes normal distribution)
In short, your concern about cpu consumption and execution time of boxing and unboxing operations is irrelevant . This is probably not a bottleneck.
The benchmark code can be found here (somewhat long code).
I have done some tests with the following code for unboxing:
And when compiled it becomes:
That is, it is something similar to:
Then the overhead is not very large, especially in terms of memory, since the "primitive" value is already in the wrapper.
Now the opposite case, autoboxing:
Which becomes:
What comes to be:
In this case, the overhead is somewhat higher, creating a new object for each primitive value (and even this may be mitigated by the cache that wrappers usually create , in such a way that if a value already has an instance of said wrapper it is reuse 1 ), but in most cases personally I would consider it acceptable.
In any case, doing "micro-optimizations" is something that should only be done if we are sure that this code is a performance bottleneck for our application: Will this method be called very often? When called, how long is the CPU processing it?
1 This is what often makes the following code work, confusing Java newbies: