Преобразование массива примитивных длин в список длин

Это может быть немного простой вопрос с гарнитурой, но моя первая попытка на удивление полностью не работала. Я хотел взять массив примитивных длин и превратить его в список, который я попытался сделать следующим образом:

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

Каков правильный способ сделать это?

Ответ 1

Мне было удобно использовать Apache Commons Lang ArrayUtils (JavaDoc, зависимость Maven)

import org.apache.commons.lang3.ArrayUtils;
...
long[] input = someAPI.getSomeLongs();
Long[] inputBoxed = ArrayUtils.toObject(input);
List<Long> inputAsList = Arrays.asList(inputBoxed);

он также имеет обратный API

long[] backToPrimitive = ArrayUtils.toPrimitive(objectArray);

РЕДАКТИРОВАТЬ: обновлено, чтобы обеспечить полное преобразование в список, как это предлагается в комментариях и других исправлениях.

Ответ 2

Так как Java 8 теперь можно использовать для этого потоки:

long[] arr = {1,2,3,4};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());

Ответ 3

import java.util.Arrays;
import org.apache.commons.lang.ArrayUtils;

List<Long> longs = Arrays.asList(ArrayUtils.toObject(new long[] {1,2,3,4}));

Ответ 4

hallidave и jpalecek имеют правильную идею итерации по массиву — но они не принимают преимущество функции, предоставляемой ArrayList: поскольку размер списка известен в этом случае, вы должны указать его при создании ArrayList.

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

Таким образом, никакие ненужные массивы создаются только для того, чтобы их отбрасывать ArrayList, потому что они оказываются слишком короткими, а пустые "слоты" теряются, потому что ArrayList переоценивает свои требования к пространству. Конечно, если вы продолжите добавлять элементы в список, вам понадобится новый массив поддержки.

Ответ 5

Немного более подробный, но это работает:

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

В вашем примере кажется, что Arrays.asList() интерпретирует ввод как список длинных [] массивов вместо списка Longs. Немного удивительно, конечно. Автобоксинг просто не работает так, как вы хотите, в этом случае.

Ответ 6

В качестве другой возможности библиотека Guava предоставляет это как Longs.asList(), с аналогичными классами утилиты для других примитивных типов.

import com.google.common.primitives.Longs;

long[] input = someAPI.getSomeLongs();
List<Long> output = Longs.asList(input);

Ответ 7

Нет, автоматическое преобразование из массива примитивного типа в массив их ссылочных типов в боксе отсутствует. Вы можете делать только

long[] input = someAPI.getSomeLongs();
List<Long> lst = new ArrayList<Long>();

for(long l : input) lst.add(l);

Ответ 8

Я пишу небольшую библиотеку для этих проблем:

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

В случае, если вы заботитесь, проверьте это здесь.

Ответ 9

Другой способ с Java 8.

long[] input = someAPI.getSomeLongs();
LongStream.of(input).boxed().collect(Collectors.toList()));

Ответ 10

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.

Необходимо сделать как минимум два важных различия:

  • Должен ли результирующий список быть представлением в массиве или это должен быть новый список
  • Должен ли результирующий список быть изменяемым или нет

Параметры будут кратко изложены здесь, а полный пример программы показан внизу этого ответа.


Создание нового списка по сравнению с созданием представления в массиве

Когда результатом должен быть новый список, тогда можно использовать один из подходов из других ответов:

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

Но следует учитывать недостатки этого: массив со значениями 1000000 long будет занимать примерно 8 мегабайт памяти. Новый список также будет занимать примерно 8 мегабайт. И, конечно же, полный массив должен быть пройден при создании этого списка. Во многих случаях создание нового списка просто не нужно. Вместо этого достаточно создать представление в массиве:

// 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 [TG43] method)

Смысл просмотра массива состоит в том, что изменения в массиве будут видны в списке:

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!

К счастью, создание копии (то есть нового списка, который не подвержен изменениям в массиве) из представления является тривиальным:

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

Теперь это верная копия, эквивалентная тому, что достигается с помощью потокового решения, показанного выше.


Создание модифицируемого представления или неизменяемого представления

Во многих случаях этого будет достаточно, когда список доступен только для чтения. Содержимое результирующего списка часто не изменяется, а только передается в последующую обработку, которая только читает список.

Разрешение на внесение изменений в список вызывает некоторые вопросы:

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?

Можно создать представление списка в массиве, который можно изменять. Это означает, что изменения в списке, такие как установка нового значения по определенному индексу, будут видны в массиве.

Но невозможно создать представление списка, которое может быть изменено структурно. Это означает, что невозможно выполнять операции, которые влияют на размер списка. Это просто потому, что размер базового массива не может быть изменен.


Ниже приведен MCVE, показывающий различные варианты реализации и возможные способы использования полученных списков:

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

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);
        class ResultList extends AbstractList<Long> implements RandomAccess
        {
            @Override
            public Long get(int index)
            {
                return array[index];
            }

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

    /**
     * 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);
        class ResultList extends AbstractList<Long> implements RandomAccess
        {
            @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;
            }
        };
        return new ResultList();
    }

}

Вывод примера показан здесь:

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

Ответ 11

Объединяя ответы Павла и Тома, мы получаем это

   @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);
            }
        };
    }

Ответ 12

Другой способ с Java 8.

final long[] a = new long[]{1L, 2L};
final List<Long> l = Arrays.stream(a).boxed().collect(Collectors.toList());

Ответ 13

Если вы хотите, чтобы подобная семантика была Arrays.asList, вам нужно будет написать (или использовать чужую) реализацию клиента List (возможно, через AbstractList). Она должна иметь ту же реализацию, что и Arrays.asList, только поле и значения unbox.

Ответ 14

Вы можете использовать transmorph:

Transmorph transmorph = new Transmorph(new DefaultConverters());
List<Long> = transmorph.convert(new long[] {1,2,3,4}, new TypeReference<List<Long>>() {});

Он также работает, если источником является массив ints, например.

Ответ 15

Я знаю, что этот вопрос достаточно стар, но... вы также можете написать свой собственный метод конверсии:

@SuppressWarnings("unchecked")
public static <T> List<T> toList(Object... items) {

    List<T> list = new ArrayList<T>();

    if (items.length == 1 && items[0].getClass().isArray()) {
        int length = Array.getLength(items[0]);
        for (int i = 0; i < length; i++) {
            Object element = Array.get(items[0], i);
            T item = (T)element;
            list.add(item);
        }
    } else {
        for (Object i : items) {
            T item = (T)i;
            list.add(item);
        }
    }

    return list;
}

После того, как вы включите его, используя статический импорт, возможные способы использования могут быть:

    long[] array = { 1, 2, 3, 4, 5, 6, 7, 8, 9 };
    List<Long> list = toList(array);

или

    List<Long> list = toList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l);

Ответ 16

Хотя можно создать новый список и добавить в него все значения (через цикл или потоки), я работал над действительно большими массивами и получаю низкую производительность. Поэтому я создал свой собственный простой в использовании класс-оболочку примитивного массива.

Пример:

long[] arr = new long[] {1,2,3};
PrimativeList<Long> list = PrimativeList.create(arr); // detects long[] and returns PrimativeList<Long>

System.out.println(list.get(1)); // prints: 2
list.set(2, 15);
System.out.println(arr[2]);  // prints: 15

Получите это здесь: https://github.com/Sf298/Sauds-Toolbox/blob/master/src/main/java/PrimitiveArrayWrapper/PrimitiveList.java

ПРИМЕЧАНИЕ. Я еще не полностью протестировал его, поэтому сообщите мне, если вы обнаружите какие-либо ошибки/проблемы.

Ответ 17

Вы можете использовать LongStream для этого

List<Long> longs = LongStream.of(new long[]{1L, 2L, 3L}).boxed()
                             .collect(Collectors.toList());