Converting Array to List

Solution 1:

The second one creates a new array of Integers (first pass), and then adds all the elements of this new array to the list (second pass). It will thus be less efficient than the first one, which makes a single pass and doesn't create an unnecessary array of Integers.

A better way to use streams would be

List<Integer> list = Arrays.stream(ints).boxed().collect(Collectors.toList());

Which should have roughly the same performance as the first one.

Note that for such a small array, there won't be any significant difference. You should try to write correct, readable, maintainable code instead of focusing on performance.

Solution 2:

Simply try something like

Arrays.asList(array)

Solution 3:

If you don't want to alter the list:

List<Integer> list = Arrays.asList(array)

But if you want to modify it then you can use this:

List<Integer> list = new ArrayList<Integer>(Arrays.asList(ints));

Or just use java8 like the following:

List<Integer> list = Arrays.stream(ints).collect(Collectors.toList());

Java9 has introduced this method:

List<Integer> list = List.of(ints);

However, this will return an immutable list that you can't add to.

You need to do the following to make it mutable:

List<Integer> list = new ArrayList<Integer>(List.of(ints));

Solution 4:

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.