Less expensive way of converting a double[] to an immutable list

273 views Asked by At

I want to convert an array double[] to an immutable collection for use in a value object. However, I am dealing with a very large array and I keep getting the error java.lang.OutOfMemoryError: Java heap space.

I am currently using

Collections.unmodifiableList(DoubleStream.of(myArray).boxed().collect(Collectors.toList()));

I think it is because of this my program is running out of memory. Is there a cheaper way to convert double[] to an immutable list?

2

There are 2 answers

3
Sweeper On

How about creating your own List<Double>? If you implement AbstractList<Double>, you'd only need to implement two methods for an unmodifiable list:

class MyDoubleList extends AbstractList<Double> implements RandomAccess {

    private double[] backingArray;

    public MyDoubleList(double[] backingArray) {
        this.backingArray = backingArray;
    }

    @Override
    public Double get(int index) {
        return backingArray[index];
    }

    @Override
    public int size() {
        return backingArray.length;
    }

    // adding other list methods should be trivial...
}

Usage:

List<Double> list = new MyDoubleList(myBigDoubleArray);

Note that if you change the backing array, the list contents will change as well. To prevent this, you'd usually copy the array passed in, but since copying the array will probably cause an out of memory exception, I didn't do it.


Or if you use Guava, use Doubles.asList(myBigDoubleArray), which does essentially the same thing. Thanks to Joe for the suggestion!

0
Orange Casper On

Streams are great for functional programming, and readability but should be avoided when performance is the main concern. They create unnecessary extra objects.

Also surprisingly, arrays of the double primitive types consume more memory than their wrapper class Double arrays (ref: https://www.baeldung.com/java-primitives-vs-objects)

Use a Double object array instead of a double primitive, and then run:

Collection<Double> l = Collections.unmodifiableCollection(Arrays.asList(myArray));

I compared the 2 approaches like this

    public static void main(String[] args) {
        int len = 1000000;
        Double[] myArray = new Double[len];
        for (int i = 0; i < len; i++) {
            myArray[i] = Math.random();
        }
        Collection<Double> l = Collections.unmodifiableCollection(Arrays.asList(myArray));
        long totalMem = Runtime.getRuntime().totalMemory();
        long usedMem =  totalMem - Runtime.getRuntime().freeMemory();
        System.out.println("totalMem="+humanReadableByteCountBin(totalMem));
        System.out.println("usedMem=" + humanReadableByteCountBin(usedMem));
        System.out.println("l has " + l.size() + " items ");
    }

The stream approach used 48Mb, whereas Arrays.asList with Double uses 28Mb.