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

140

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

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

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

Брэндон Ярбро
источник
6
Я думаю, что у нас был тот же вопрос для int, не так ли?
Том Хотин - tackline

Ответы:

115

Мне было удобно использовать 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);

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

Эран Медан
источник
4
Учитывая, что это создает массив Long, а не List , он не отвечает на вопрос OP и получает мой голос против. Как, черт возьми, это получило 56 голосов и желанный «чек» ???
user949300
7
Потому что люди, Arrays.asList(ArrayUtils.toObject(input))наверное, легко могут это сделать .
Эран Медан,
1
Я согласен с @ user949300. Это не отвечает на вопрос.
dev4life
1
Этот ответ следует обновить, чтобы обеспечить полное преобразование в список. Однако это эффективный шаг к решению.
Джим Джефферс
2
@JimJeffers - спасибо, обновлено, чтобы включить полную конвертацию. Поскольку OP включен List<Long> = Arrays.asList(inputBoxed)в их вопрос, я счел излишним повторять его, поскольку я думал, что это было очевидно, я думаю, я был неправ ...
Эран Медан
115

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

long[] arr = {1,2,3,4};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
Марцинь
источник
7
Хороший! К сожалению, и это несколько загадочно, streamфункция определена только для int[], long[]и double[].
Norswap
1
В качестве альтернативы вы можете использовать LongStream.of(arr).boxed()....
aioobe
2
Arrays.stream(arr).boxed().collect(Collectors.toList());К сожалению, это может только вернутьсяList<Object>
Сентилкумар Аннадурай
Никаких громоздких библиотек для простой задачи, никаких циклов. Отличный ответ.
Alex
36
import java.util.Arrays;
import org.apache.commons.lang.ArrayUtils;

List<Long> longs = Arrays.asList(ArrayUtils.toObject(new long[] {1,2,3,4}));
Марко Пелегрини
источник
6
Было бы полезно объяснить, что это делает и является ли это сторонней библиотекой.
Игорь Ганапольский
35

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

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

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

Эриксон
источник
1
Я стараюсь опускать спецификацию длины, если только не будет доказано, что код является частью горячей точки производительности или если ожидается, что массив будет чрезвычайно большим. Я думаю, что отсутствие длины делает код более читабельным.
Hallidave
19

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

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

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

Hallidave
источник
18

В качестве другой возможности библиотека Guava предоставляет это as 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);
jpalecek
источник
7

Задан вопрос о том, как превратить массив в список. Большинство ответов до сих пор показывали, как создать новый список с тем же содержимым, что и массив, или со ссылкой на сторонние библиотеки. Однако для такого преобразования есть простые встроенные опции. Некоторые из них уже были обрисованы в других ответах (например, в этом ). Но я хотел бы указать и подробно описать определенные степени свободы для реализации здесь, а также показать потенциальные преимущества, недостатки и предостережения.

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

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

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


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

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

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

(См. Пример внизу для реализации toListметода)

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

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
Marco13
источник
6

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

long[] input = someAPI.getSomeLongs();
LongStream.of(input).boxed().collect(Collectors.toList()));
равенскейтер
источник
6

Я пишу небольшую библиотеку для этих задач:

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

Если вам интересно, проверьте это здесь .

DFA
источник
хорошая библиотека! сначала я не был уверен, что это Java ... Мне нравится стиль JQuery
Яник Рочон
4

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

final long[] a = new long[]{1L, 2L};
final List<Long> l = Arrays.stream(a).boxed().collect(Collectors.toList());
Джин Квон
источник
1
Возвращая список (не присваивая его), я обнаружил, что должен сделать:Arrays.stream(a).boxed().collect(Collectors.<Long>toList());
Джон
1
Это идентично существующему ответу .
Панг
3

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

   @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);
            }
        };
    }
Дункан МакГрегор
источник
2

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

Том Хотин - tackline
источник
2

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

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

Это также работает, например, если источник - это массив целых чисел.

чабануа
источник
2

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

@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);
Павел Нетеса
источник
2
по поводу:, catch (ArrayIndexOutOfBoundsException ex) { /* Finished getting array elements */ }ты ужасный человек.
Брэндон Ярбро,
Эй, мужик, спасибо за это! Ваше ироничное замечание заставило меня найти лучшее решение - получить длину массива через Array.getLength ().
Павел Нетеса
1
Фантастика! Я рад, что мое сардоническое отношение привело к прогрессу, а не просто плохие чувства всех вокруг :) На самом деле, использовать исключения, за исключением очень необычных условий, - не лучшая идея. Их создание на удивление дорого. Эта новая версия намного, НАМНОГО быстрее.
Брэндон Ярбро,
1

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

Пример:

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

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

sf298
источник
0

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

List<Long> longs = LongStream.of(new long[]{1L, 2L, 3L}).boxed()
                             .collect(Collectors.toList());
Ритам Чакраборти
источник