If you don't mind a third-party dependency, you could use a library which natively supports primitive collections like Eclipse Collections and avoid the boxing altogether. You can also use primitive collections to create boxed regular collections if you need to.
int[] ints = {1, 2, 3};
MutableIntList intList = IntLists.mutable.with(ints);
List<Integer> list = intList.collect(Integer::valueOf);
If you want the boxed collection in the end, this is what the code for collect
on IntArrayList
is doing under the covers:
public <V> MutableList<V> collect(IntToObjectFunction<? extends V> function)
{
return this.collect(function, FastList.newList(this.size));
}
public <V, R extends Collection<V>> R collect(IntToObjectFunction<? extends V> function,
R target)
{
for (int i = 0; i < this.size; i++)
{
target.add(function.valueOf(this.items[i]));
}
return target;
}
Since the question was specifically about performance, I wrote some JMH benchmarks using your solutions, the most voted answer and the primitive and boxed versions of Eclipse Collections.
import org.eclipse.collections.api.list.primitive.IntList;
import org.eclipse.collections.impl.factory.primitive.IntLists;
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
@State(Scope.Thread)
@BenchmarkMode(Mode.Throughput)
@OutputTimeUnit(TimeUnit.SECONDS)
@Fork(2)
public class IntegerArrayListFromIntArray
{
private int[] source = IntStream.range(0, 1000).toArray();
public static void main(String[] args) throws RunnerException
{
Options options = new OptionsBuilder().include(
".*" + IntegerArrayListFromIntArray.class.getSimpleName() + ".*")
.forks(2)
.mode(Mode.Throughput)
.timeUnit(TimeUnit.SECONDS)
.build();
new Runner(options).run();
}
@Benchmark
public List<Integer> jdkClassic()
{
List<Integer> list = new ArrayList<>(source.length);
for (int each : source)
{
list.add(each);
}
return list;
}
@Benchmark
public List<Integer> jdkStreams1()
{
List<Integer> list = new ArrayList<>(source.length);
Collections.addAll(list,
Arrays.stream(source).boxed().toArray(Integer[]::new));
return list;
}
@Benchmark
public List<Integer> jdkStreams2()
{
return Arrays.stream(source).boxed().collect(Collectors.toList());
}
@Benchmark
public IntList ecPrimitive()
{
return IntLists.immutable.with(source);
}
@Benchmark
public List<Integer> ecBoxed()
{
return IntLists.mutable.with(source).collect(Integer::valueOf);
}
}
These are the results from these tests on my Mac Book Pro. The units are operations per second, so the bigger the number, the better. I used an ImmutableIntList
for the ecPrimitive
benchmark, because the MutableIntList
in Eclipse Collections doesn't copy the array by default. It merely adapts the array you give it. This was reporting even larger numbers for ecPrimitive
, with a very large margin of error because it was essentially measuring the cost of a single object creation.
# Run complete. Total time: 00:06:52
Benchmark Mode Cnt Score Error Units
IntegerArrayListFromIntArray.ecBoxed thrpt 40 191671.859 ± 2107.723 ops/s
IntegerArrayListFromIntArray.ecPrimitive thrpt 40 2311575.358 ± 9194.262 ops/s
IntegerArrayListFromIntArray.jdkClassic thrpt 40 138231.703 ± 1817.613 ops/s
IntegerArrayListFromIntArray.jdkStreams1 thrpt 40 87421.892 ± 1425.735 ops/s
IntegerArrayListFromIntArray.jdkStreams2 thrpt 40 103034.520 ± 1669.947 ops/s
If anyone spots any issues with the benchmarks, I'll be happy to make corrections and run them again.
Note: I am a committer for Eclipse Collections.