Перетворіть масив примітивних довжин у Список довгих


138

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

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

Який правильний спосіб зробити це?


6
Я думаю, у нас було те саме питання щодо ints, чи не так?
Том Хотін - тайклін

Відповіді:


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

EDIT: оновлено, щоб забезпечити повне перетворення до списку, як пропонують коментарі та інші виправлення.


3
Зважаючи на те, що це створює масив Лонгів, а не Список , він не відповідає на питання ОП і отримує мою оцінку. Як чорт зробив це, отримавши 56 оновлень і заповіту "чек" ???
user949300

7
Тому що люди можуть легко зробити це, Arrays.asList(ArrayUtils.toObject(input))мабуть.
Еран Медан

Я згоден з @ user949300. Це не відповідає на запитання.
dev4life

1
Цю відповідь слід оновити, щоб забезпечити повне перетворення до списку. Однак це ефективний крок до рішення.
Джим Джефферс

2
@JimJeffers - дякую, оновлено до включеного повного перетворення. Оскільки ОП, включений List<Long> = Arrays.asList(inputBoxed)у їхнє запитання, мені здалося зайвим повторювати це, оскільки я вважав це очевидним, я думаю, я помилявся ...
Eran Medan

114

Оскільки 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>
Sentiilkumar Annadurai

Ні об'ємних бібліотек для простого завдання, ні циклів. Чудова відповідь.
Alex Quilliam


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 () інтерпретує введення як список довгих [] масивів замість списку Longs. Трохи дивно, точно. Автобоксинг просто не працює так, як ви хочете в цьому випадку.



7

Ні, немає автоматичного перетворення з масиву примітивного типу в масив їх посилань у коробці. Ви можете тільки робити

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

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

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

6

Ще один спосіб з Java 8.

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

6

Я пишу невеличку бібліотеку для цих проблем:

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

У випадку, коли вас турбує, перевірте це тут .


приємна бібліотека! спочатку я не був впевнений, що це Java ... Мені подобається стиль JQuery
Yanick Rochon,


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і значення значень лише для поля та розпакування.


2

Можна використовувати трансморф :

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

Він також працює, якщо джерелом є масив ints, наприклад.


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

Хоча можна створити новий Список і додати до нього всі значення (via для циклу або потоків), я працював над дійсно великими масивами і отримую низьку продуктивність. Тому я створив власний простий у користуванні клас примітивної обертки для масиву.

Приклад:

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

ПРИМІТКА: Я ще не повністю його перевірив, тому повідомте мені, якщо ви знайшли якісь помилки / проблеми.


0

Ви можете використовувати LongStreamдля цього

List<Long> longs = LongStream.of(new long[]{1L, 2L, 3L}).boxed()
                             .collect(Collectors.toList());
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.