Convert an array of primitive longs into a List of

2019-01-01 09:18发布

This may be a bit of an easy, headdesk sort of question, but my first attempt surprisingly completely failed to work. I wanted to take an array of primitive longs and turn it into a list, which I attempted to do like this:

long[] input = someAPI.getSomeLongs();
List<Long> inputAsList = Arrays.asList(input); //Total failure to even compile!

What's the right way to do this?

15条回答
十年一品温如言
2楼-- · 2019-01-01 09:31

Another way with Java 8.

long[] input = someAPI.getSomeLongs();
LongStream.of(input).boxed().collect(Collectors.toList()));
查看更多
柔情千种
3楼-- · 2019-01-01 09:32

hallidave and jpalecek have the right idea—iterating over an array—but they don't take advantage of a feature provided by ArrayList: since the size of the list is known in this case, you should specify it when you create the ArrayList.

List<Long> list = new ArrayList<Long>(input.length);
for (long n : input)
  list.add(n);

This way, no unnecessary arrays are created only to be discarded by the ArrayList because they turn out to be too short, and no empty "slots" are wasted because ArrayList overestimated its space requirements. Of course, if you continue to add elements to the list, a new backing array will be needed.

查看更多
听够珍惜
4楼-- · 2019-01-01 09:33

The question asked about how to turn an array into a list. Most answers so far showed how to create a new list with the same contents as the array, or referred to third-party libraries. However, there are simple, built-in options for this sort of conversion. Some of them have already been sketched in other answers (e.g. this one). But I'd like to point out and elaborate certain degrees of freedom for the implementation here, and show the potential benefits, drawbacks and caveats.

There are at least two important distinctions to be made:

  • Whether the resulting list should be a view on the array or whether it should be a new list
  • Whether the resulting list should be modifiable or not

The options will be summarized here quickly, and a complete example program is shown at the bottom of this answer.


Creating a new list versus creating a view on the array

When the result should be a new list, then one of the approaches from the other answers may be used:

List<Long> list = Arrays.stream(array).boxed().collect(Collectors.toList());

But one should consider the drawbacks of doing this: An array with 1000000 long values will occupy roughly 8 Megabytes of memory. The new list will also occupy roughly 8 Megabytes. And of course, the full array has to be traversed while creating this list. In many cases, creating a new list is simply not necessary. Instead, it is sufficient to create a view on the array:

// This occupies ca. 8 MB
long array[] = { /* 1 million elements */ }

// Properly implemented, this list will only occupy a few bytes,
// and the array does NOT have to be traversed, meaning that this
// operation has nearly ZERO memory- and processing overhead:
List<Long> list = asList(array);

(See the example at the bottom for an implementation of the toList method)

The implication of having a view on the array are that changes in the array will be visible in the list:

long array[] = { 12, 34, 56, 78 };
List<Long> list = asList(array);

System.out.println(list.get(1)); // This will print 34

// Modify the array contents:
array[1] = 12345;

System.out.println(list.get(1)); // This will now print 12345!

Fortunately, creating a copy (that is, a new list that is not affected by modifications in the array) from the view is trivial:

List<Long> copy = new ArrayList<Long>(asList(array));

Now, this is a true copy, equivalent to what is achieved with the stream-based solution that was shown above.


Creating a modifiable view or an unmodifiable view

In many cases, it will be sufficient when the list is read-only. The contents of the resulting list will often not be modified, but only passed to downstream processing that only reads the list.

Allowing for modifications of the list raises some questions:

long array[] = { 12, 34, 56, 78 };
List<Long> list = asList(array);

list.set(2, 34567);           // Should this be possible?
System.out.println(array[2]); // Should this print 34567?
list.set(3, null);            // What should happen here?
list.add(99999);              // Should this be possible?

It is possible to create a list view on the array that is modifiable. This means that changes in the list, like setting a new value at a certain index, will be visible in the array.

But it is not possible to create a list view that is structurally modifiable. This means that it is not possible to do operations that affect the size of the list. This is simply because the size of the underlying array cannot be changed.


The following is a MCVE showing the different implementation options, and the possible ways of using the resulting lists:

import java.util.AbstractList;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;

public class PrimitiveArraysAsLists
{
    public static void main(String[] args)
    {
        long array[] = { 12, 34, 56, 78 };

        // Create VIEWS on the given array
        List<Long> list = asList(array);
        List<Long> unmodifiableList = asUnmodifiableList(array);

        // If a NEW list is desired (and not a VIEW on the array), this
        // can be created as well:
        List<Long> copy = new ArrayList<Long>(asList(array));

        System.out.println("array           : " + Arrays.toString(array));
        System.out.println("list            : " + list);
        System.out.println("unmodifiableList: " + unmodifiableList);
        System.out.println("copy            : " + copy);        

        // Modify a value in the array. The changes will be visible
        // in the list and the unmodifiable list, but not in
        // the copy.
        System.out.println("Changing value at index 1 of the array...");
        array[1] = 34567;

        System.out.println("array           : " + Arrays.toString(array));
        System.out.println("list            : " + list);
        System.out.println("unmodifiableList: " + unmodifiableList);
        System.out.println("copy            : " + copy);        

        // Modify a value of the list. The changes will be visible
        // in the array and the unmodifiable list, but not in
        // the copy.
        System.out.println("Changing value at index 2 of the list...");
        list.set(2, 56789L);

        System.out.println("array           : " + Arrays.toString(array));
        System.out.println("list            : " + list);
        System.out.println("unmodifiableList: " + unmodifiableList);
        System.out.println("copy            : " + copy);        


        // Certain operations are not supported:
        try
        {
            // Throws an UnsupportedOperationException: This list is 
            // unmodifiable, because the "set" method is not implemented
            unmodifiableList.set(2, 23456L);
        }
        catch (UnsupportedOperationException e) 
        {
            System.out.println("Expected: " + e);
        }

        try
        {
            // Throws an UnsupportedOperationException: The size of the
            // backing array cannot be changed
            list.add(90L);
        }
        catch (UnsupportedOperationException e) 
        {
            System.out.println("Expected: " + e);
        }


        try
        {
            // Throws a NullPointerException: The value 'null' cannot be  
            // converted to a primitive 'long' value for the underlying array
            list.set(2, null);
        }
        catch (NullPointerException e)
        {
            System.out.println("Expected: " + e);
        }

    }

    /**
     * Returns an unmodifiable view on the given array, as a list.
     * Changes in the given array will be visible in the returned
     * list.
     *  
     * @param array The array
     * @return The list view
     */
    private static List<Long> asUnmodifiableList(long array[])
    {
        Objects.requireNonNull(array);
        return new AbstractList<Long>()
        {
            @Override
            public Long get(int index)
            {
                return array[index];
            }

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

    /**
     * Returns a view on the given array, as a list. Changes in the given 
     * array will be visible in the returned list, and vice versa. The
     * list does not allow for <i>structural modifications</i>, meaning
     * that it is not possible to change the size of the list.
     *  
     * @param array The array
     * @return The list view
     */
    private static List<Long> asList(long array[])
    {
        Objects.requireNonNull(array);
        return new AbstractList<Long>()
        {
            @Override
            public Long get(int index)
            {
                return array[index];
            }

            @Override
            public Long set(int index, Long element)
            {
                long old = array[index];
                array[index] = element;
                return old;
            }

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

}

The output of the example is shown here:

array           : [12, 34, 56, 78]
list            : [12, 34, 56, 78]
unmodifiableList: [12, 34, 56, 78]
copy            : [12, 34, 56, 78]
Changing value at index 1 of the array...
array           : [12, 34567, 56, 78]
list            : [12, 34567, 56, 78]
unmodifiableList: [12, 34567, 56, 78]
copy            : [12, 34, 56, 78]
Changing value at index 2 of the list...
array           : [12, 34567, 56789, 78]
list            : [12, 34567, 56789, 78]
unmodifiableList: [12, 34567, 56789, 78]
copy            : [12, 34, 56, 78]
Expected: java.lang.UnsupportedOperationException
Expected: java.lang.UnsupportedOperationException
Expected: java.lang.NullPointerException
查看更多
素衣白纱
5楼-- · 2019-01-01 09:35

I'm writing a small library for these problems:

long[] input = someAPI.getSomeLongs();
List<Long> = $(input).toList();

In the case you care check it here.

查看更多
无与为乐者.
6楼-- · 2019-01-01 09:35

Combining Pavel and Tom's answers we get this

   @SuppressWarnings("unchecked")
    public static <T> List<T> asList(final Object array) {
        if (!array.getClass().isArray())
            throw new IllegalArgumentException("Not an array");
        return new AbstractList<T>() {
            @Override
            public T get(int index) {
                return (T) Array.get(array, index);
            }

            @Override
            public int size() {
                return Array.getLength(array);
            }
        };
    }
查看更多
时光乱了年华
7楼-- · 2019-01-01 09:37

A bit more verbose, but this works:

    List<Long> list = new ArrayList<Long>();
    for (long value : input) {
        list.add(value);
    }

In your example it appears that Arrays.asList() is interpreting the input as list of long[] arrays instead of a list of Longs. A bit surprising, for sure. Autoboxing just doesn't work the way you want it to in this case.

查看更多
登录 后发表回答