Konwertuj tablicę prymitywnych długich na listę długich

138

To może być trochę łatwe pytanie w rodzaju headdesku, ale moja pierwsza próba zaskakująco kompletnie się nie powiodła. Chciałem wziąć tablicę prymitywnych tęsknot i przekształcić ją w listę, którą próbowałem zrobić w następujący sposób:

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

Jaki jest właściwy sposób, aby to zrobić?

Brandon Yarbrough
źródło
6
Myślę, że mieliśmy to samo pytanie dla intów, prawda?
Tom Hawtin - tackline

Odpowiedzi:

115

Uważam, że wygodnie jest używać apache commons lang ArrayUtils ( zależność JavaDoc , Maven )

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

ma również odwrotne API

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

EDYCJA: zaktualizowano, aby zapewnić pełną konwersję do listy, zgodnie z sugestiami i innymi poprawkami.

Eran Medan
źródło
3
Biorąc pod uwagę, że tworzy to tablicę Longs, a nie List , nie odpowiada na pytanie OP i otrzymuje mój głos przeciw. Jak do cholery to dostało 56 głosów pozytywnych i upragniony "czek" ???
user949300
7
Ponieważ ludzie Arrays.asList(ArrayUtils.toObject(input))prawdopodobnie mogą to łatwo zrobić .
Eran Medan
Zgadzam się z @ user949300. To nie odpowiada na pytanie.
dev4life
1
Ta odpowiedź powinna zostać zaktualizowana, aby zapewnić pełną konwersję do listy. Jest to jednak skuteczny krok w kierunku rozwiązania.
Jim Jeffers
2
@JimJeffers - dzięki, zaktualizowano, aby uwzględnić pełną konwersję. Ponieważ OP zawarty List<Long> = Arrays.asList(inputBoxed)w ich pytaniu uznałem za zbędne powtarzanie go, ponieważ myślałem, że to oczywiste, myślę, że się myliłem ...
Eran Medan
114

Od wersji Java 8 możesz teraz używać do tego strumieni:

long[] arr = {1,2,3,4};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
marcinj
źródło
7
Niezłe! Niestety, i nieco tajemniczo, streamfunkcja jest zdefiniowana tylko dla int[], long[]i double[].
Norswap
1
Alternatywnie możesz użyć LongStream.of(arr).boxed()....
aioobe
2
Arrays.stream(arr).boxed().collect(Collectors.toList());Niestety to może powrócićList<Object>
Senthilkumar Annadurai
Żadnych obszernych bibliotek do prostego zadania, żadnych pętli. Świetna odpowiedź.
Alex Quilliam
37
import java.util.Arrays;
import org.apache.commons.lang.ArrayUtils;

List<Long> longs = Arrays.asList(ArrayUtils.toObject(new long[] {1,2,3,4}));
Marco Pelegrini
źródło
5
Pomocne byłoby wyjaśnienie, co to robi i czy jest to biblioteka strony trzeciej.
Igor Ganapolsky
35

hallidave i jpalecek mają dobry pomysł - iterując po tablicy - ale nie korzystają z funkcji dostarczonej przez ArrayList: ponieważ rozmiar listy jest w tym przypadku znany , powinieneś określić go podczas tworzenia ArrayList.

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

W ten sposób żadne niepotrzebne tablice nie są tworzone tylko po to, aby je wyrzucić, ArrayListponieważ okazują się zbyt krótkie, a puste "gniazda" nie są marnowane, ponieważ ArrayListprzeszacowano jego wymagania przestrzenne. Oczywiście, jeśli będziesz nadal dodawać elementy do listy, potrzebna będzie nowa tablica zapasowa.

erickson
źródło
1
Zwykle pomijam specyfikację długości, chyba że udowodniono, że kod jest częścią gorącego punktu wydajności lub oczekuje się, że tablica będzie bardzo duża. Myślę, że pominięcie długości sprawia, że ​​kod jest nieco bardziej czytelny.
hallidave
19

Nieco bardziej szczegółowe, ale to działa:

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

W twoim przykładzie wygląda na to, że Arrays.asList () interpretuje dane wejściowe jako listę tablic long [] zamiast listy Longs. Na pewno trochę zaskakujące. W tym przypadku autoboxing po prostu nie działa tak, jak tego chcesz.

hallidave
źródło
17

Jako kolejną możliwość, biblioteka Guava zapewnia to podobnie jak Longs.asList()w przypadku podobnych klas użytkowych dla innych typów pierwotnych.

import com.google.common.primitives.Longs;

long[] input = someAPI.getSomeLongs();
List<Long> output = Longs.asList(input);
Trevor Robinson
źródło
7

Nie, nie ma automatycznej konwersji z tablicy typu pierwotnego na tablicę ich opakowanych typów odwołań. Możesz tylko to zrobić

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

for(long l : input) lst.add(l);
jpalecek
źródło
7

Pytanie dotyczyło tego, jak zamienić tablicę w listę. Większość dotychczasowych odpowiedzi pokazywała, jak utworzyć nową listę z taką samą zawartością jak tablica lub z odniesieniami do bibliotek innych firm. Istnieją jednak proste, wbudowane opcje tego rodzaju konwersji. Niektóre z nich zostały już naszkicowane w innych odpowiedziach (np. W tej ). Chciałbym jednak wskazać i rozwinąć tutaj pewne stopnie swobody dla implementacji oraz pokazać potencjalne korzyści, wady i zastrzeżenia.

Należy dokonać co najmniej dwóch ważnych rozróżnień:

  • Czy wynikowa lista powinna być widokiem tablicy, czy też powinna to być nowa lista
  • Czy wynikowa lista powinna być modyfikowalna, czy nie

Opcje zostaną tutaj szybko podsumowane, a pełny przykładowy program jest pokazany na dole tej odpowiedzi.


Tworzenie nowej listy a tworzenie widoku na tablicy

Jeśli wynikiem powinna być nowa lista, można zastosować jedno z podejść z innych odpowiedzi:

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

Należy jednak wziąć pod uwagę wady takiego działania: tablica zawierająca 1000000 longwartości zajmie około 8 megabajtów pamięci. Nowa lista zajmie również około 8 megabajtów. Oczywiście podczas tworzenia tej listy należy przejść przez całą tablicę. W wielu przypadkach tworzenie nowej listy po prostu nie jest konieczne. Zamiast tego wystarczy stworzyć widok na tablicy:

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

(Zobacz przykład na dole, aby zobaczyć implementację toListmetody)

Konsekwencją posiadania widoku tablicy jest to, że zmiany w tablicy będą widoczne na liście:

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!

Na szczęście utworzenie kopii (czyli nowej listy, na którą nie mają wpływu modyfikacje tablicy) z widoku jest trywialne:

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

To jest prawdziwa kopia, równoważna temu, co osiągnięto dzięki rozwiązaniu opartemu na strumieniu, które zostało pokazane powyżej.


Tworzenie modyfikowalnego widoku lub niemodyfikowalnego widoku

W wielu przypadkach wystarczy, że lista jest tylko do odczytu . Zawartość listy wynikowej często nie jest modyfikowana, a jedynie przekazywana do dalszego przetwarzania, które tylko odczytuje listę.

Dopuszczenie modyfikacji listy rodzi kilka pytań:

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?

Możliwe jest utworzenie widoku listy w tablicy, którą można modyfikować . Oznacza to, że zmiany na liście, takie jak ustawienie nowej wartości w określonym indeksie, będą widoczne w tablicy.

Nie jest jednak możliwe utworzenie widoku listy, który można modyfikować strukturalnie . Oznacza to, że nie można wykonywać operacji, które mają wpływ na rozmiar listy. Dzieje się tak po prostu dlatego, że nie można zmienić rozmiaru tablicy bazowej .


Poniżej znajduje się MCVE pokazujący różne opcje implementacji i możliwe sposoby wykorzystania wynikowych list:

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

}

Dane wyjściowe przykładu pokazano tutaj:

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
źródło
6

Inny sposób z Javą 8.

long[] input = someAPI.getSomeLongs();
LongStream.of(input).boxed().collect(Collectors.toList()));
ravenskater
źródło
6

Piszę małą bibliotekę dla tych problemów:

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

Jeśli chcesz, sprawdź to tutaj .

dfa
źródło
ładna biblioteka! na początku nie byłem pewien, czy to Java ... Podoba mi się styl JQuery
Yanick Rochon
4

Inny sposób z Javą 8.

final long[] a = new long[]{1L, 2L};
final List<Long> l = Arrays.stream(a).boxed().collect(Collectors.toList());
Jin Kwon
źródło
1
Zwracając listę (nie przypisując jej), stwierdziłem, że muszę:Arrays.stream(a).boxed().collect(Collectors.<Long>toList());
Jon
1
Jest to identyczne z tą istniejącą odpowiedzią .
Pang
3

Łącząc odpowiedzi Pawła i Toma otrzymujemy to

   @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);
            }
        };
    }
Duncan McGregor
źródło
2

Jeśli chcesz mieć podobną semantykę Arrays.asList, musisz napisać (lub użyć cudzej) implementacji klienta List(prawdopodobnie przez AbstractList. Powinna mieć taką samą implementację, jak Arrays.asListtylko wartości box i unbox).

Tom Hawtin - haczyk
źródło
2

Możesz użyć transmorfii :

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

Działa również, jeśli source jest na przykład tablicą liczb int.

cchabanois
źródło
2

Wiem, że to pytanie jest wystarczająco stare, ale ... możesz też napisać własną metodę konwersji:

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

Po dołączeniu go za pomocą importu statycznego możliwe zastosowania mogą być:

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

lub

    List<Long> list = toList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l);
Pavel Netesa
źródło
2
odnośnie: catch (ArrayIndexOutOfBoundsException ex) { /* Finished getting array elements */ }jesteś okropną osobą.
Brandon Yarbrough
Hej, stary, dzięki za to! Twoja ironiczna uwaga sprawiła, że ​​znalazłem lepsze rozwiązanie - uzyskanie długości tablicy za pomocą funkcji Array.getLength ().
Pavel Netesa,
1
Fantastyczny! Cieszę się, że moja sardoniczna postawa doprowadziła do postępu, a nie tylko ogólnego złego samopoczucia dookoła :) Naprawdę, nie jest dobrym pomysłem używanie wyjątków, z wyjątkiem bardzo nietypowych warunków. Są zaskakująco drogie w tworzeniu. Ta nowa wersja jest dużo, DUŻO szybsza.
Brandon Yarbrough,
1

Chociaż możliwe jest utworzenie nowej listy i dodanie do niej wszystkich wartości (za pośrednictwem pętli for lub strumieni), pracowałem nad naprawdę dużymi tablicami i uzyskiwałem słabą wydajność. Dlatego stworzyłem własną, łatwą w użyciu, prostą klasę opakowania tablicy.

Przykład:

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

Pobierz tutaj: https://github.com/Sf298/Sauds-Toolbox/blob/master/src/main/java/PrimitiveArrayWrapper/PrimitiveList.java

UWAGA: nie przetestowałem go jeszcze w pełni, więc daj mi znać, jeśli znajdziesz jakieś błędy / problemy.

sf298
źródło
0

Możesz użyć LongStreamdo tego

List<Long> longs = LongStream.of(new long[]{1L, 2L, 3L}).boxed()
                             .collect(Collectors.toList());
Ritam Chakraborty
źródło