Ubah susunan long primitive menjadi List of Longs

138

Ini mungkin agak mudah, semacam pertanyaan utama, tetapi upaya pertama saya secara mengejutkan gagal total. Saya ingin mengambil deretan panjang primitif dan mengubahnya menjadi daftar, yang saya coba lakukan seperti ini:

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

Apa cara yang benar untuk melakukan ini?

Brandon Yarbrough
sumber
6
Saya pikir kami memiliki pertanyaan yang sama untuk int, bukan?
Tom Hawtin - tackline

Jawaban:

115

Saya merasa nyaman untuk melakukan menggunakan apache commons lang ArrayUtils ( javadoc , Maven ketergantungan )

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

itu juga memiliki API terbalik

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

EDIT: diperbarui untuk memberikan konversi lengkap ke daftar seperti yang disarankan oleh komentar dan perbaikan lainnya.

Eran Medan
sumber
3
Menimbang bahwa ini menciptakan array Long, bukan Daftar , itu tidak menjawab pertanyaan OP dan mendapatkan downvote saya. Bagaimana sih ini bisa mendapatkan upvotes 56 dan "cek" didambakan ???
user949300
7
Karena orang bisa dengan mudah melakukannya Arrays.asList(ArrayUtils.toObject(input)).
Eran Medan
Saya setuju dengan @ user949300. Ini tidak menjawab pertanyaan.
dev4life
1
Jawaban ini harus diperbarui untuk menyediakan konversi lengkap ke daftar. Namun, ini merupakan langkah efisien menuju solusi.
Jim Jeffers
2
@JimJeffers - terima kasih, diperbarui untuk memasukkan konversi lengkap. Karena OP memasukkannya List<Long> = Arrays.asList(inputBoxed)dalam pertanyaan mereka, saya merasa berlebihan untuk mengulanginya karena saya pikir itu sudah jelas, saya kira saya salah ...
Eran Medan
114

Karena Java 8 sekarang Anda dapat menggunakan stream untuk itu:

long[] arr = {1,2,3,4};
List<Long> list = Arrays.stream(arr).boxed().collect(Collectors.toList());
marcinj
sumber
7
Yang bagus! Sayangnya, dan agak misterius, streamfungsi ini hanya ditentukan untuk int[], long[]dan double[].
Norswap
1
Atau, Anda bisa menggunakan LongStream.of(arr).boxed()....
aioobe
2
Arrays.stream(arr).boxed().collect(Collectors.toList());Sayangnya ini hanya dapat kembaliList<Object>
Senthilkumar Annadurai
Tidak ada perpustakaan besar untuk tugas sederhana, tidak ada loop. Jawaban yang bagus
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
sumber
5
Penjelasan tentang apa yang dilakukan dan apakah ini adalah perpustakaan pihak ketiga akan sangat membantu.
IgorGanapolsky
35

hallidave dan jpalecek memiliki ide yang tepat — iterasi di atas array — tetapi mereka tidak memanfaatkan fitur yang disediakan oleh ArrayList: karena ukuran daftar diketahui dalam kasus ini, Anda harus menentukannya saat membuat ArrayList.

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

Dengan cara ini, tidak ada array yang tidak perlu dibuat hanya untuk dibuang oleh ArrayListkarena mereka ternyata terlalu pendek, dan tidak ada "slot" kosong yang terbuang karena ArrayListterlalu tinggi kebutuhan ruangnya. Tentu saja, jika Anda terus menambahkan elemen ke daftar, array dukungan baru akan diperlukan.

erickson
sumber
1
Saya cenderung mengabaikan spesifikasi panjang kecuali kode terbukti menjadi bagian dari hot spot kinerja atau array diharapkan sangat besar. Saya pikir meninggalkan panjangnya membuat kode sedikit lebih mudah dibaca.
Hallidave
19

Sedikit lebih bertele-tele, tetapi ini berfungsi:

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

Dalam contoh Anda, terlihat bahwa Array.asList () menafsirkan input sebagai daftar array panjang [] alih-alih daftar Long. Agak mengejutkan, pasti. Autoboxing tidak berfungsi seperti yang Anda inginkan dalam hal ini.

hallidave
sumber
17

Sebagai kemungkinan lain, perpustakaan Guava menyediakan ini sebagai Longs.asList(), dengan kelas utilitas serupa untuk tipe primitif lainnya.

import com.google.common.primitives.Longs;

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

Tidak, tidak ada konversi otomatis dari array tipe primitif ke array tipe referensi kotaknya. Anda hanya bisa melakukannya

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

for(long l : input) lst.add(l);
jpalecek
sumber
7

Pertanyaan yang diajukan tentang cara mengubah array menjadi daftar. Sebagian besar jawaban sejauh ini menunjukkan cara membuat daftar baru dengan konten yang sama dengan array, atau merujuk ke perpustakaan pihak ketiga. Namun, ada opsi sederhana dan bawaan untuk konversi semacam ini. Beberapa dari mereka sudah dibuat sketsa di jawaban lain (misalnya yang ini ). Tetapi saya ingin menunjukkan dan menguraikan derajat kebebasan tertentu untuk implementasi di sini, dan menunjukkan potensi manfaat, kelemahan, dan peringatan.

Setidaknya ada dua perbedaan penting yang harus dibuat:

  • Apakah daftar yang dihasilkan harus berupa tampilan pada array atau apakah itu harus daftar baru
  • Apakah daftar yang dihasilkan harus dimodifikasi atau tidak

Opsi akan diringkas di sini dengan cepat, dan program contoh lengkap ditampilkan di bagian bawah jawaban ini.


Membuat daftar baru versus membuat tampilan pada array

Ketika hasilnya harus menjadi daftar baru , maka salah satu pendekatan dari jawaban lain dapat digunakan:

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

Tetapi orang harus mempertimbangkan kelemahan dari melakukan ini: Sebuah array dengan longnilai 1000000 akan menempati sekitar 8 Megabytes memori. Daftar baru juga akan menempati kira-kira 8 Megabita. Dan tentu saja, array lengkap harus dilalui saat membuat daftar ini. Dalam banyak kasus, membuat daftar baru sama sekali tidak perlu. Sebagai gantinya, cukup membuat tampilan pada 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);

(Lihat contoh di bawah untuk implementasi toListmetode ini)

Implikasi memiliki tampilan pada array adalah bahwa perubahan dalam array akan terlihat dalam daftar:

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!

Untungnya, membuat salinan (yaitu, daftar baru yang tidak terpengaruh oleh modifikasi dalam array) dari tampilan sepele:

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

Sekarang, ini adalah salinan yang benar, setara dengan apa yang dicapai dengan solusi berbasis aliran yang ditunjukkan di atas.


Membuat dimodifikasi pandangan atau unmodifiable tampilan

Dalam banyak kasus, itu akan cukup ketika daftar hanya-baca . Isi daftar yang dihasilkan sering tidak akan diubah, tetapi hanya diteruskan ke pemrosesan hilir yang hanya membaca daftar.

Mengizinkan modifikasi daftar menimbulkan beberapa pertanyaan:

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?

Dimungkinkan untuk membuat tampilan daftar pada array yang dapat dimodifikasi . Ini berarti bahwa perubahan dalam daftar, seperti pengaturan nilai baru pada indeks tertentu, akan terlihat dalam array.

Tetapi tidak mungkin untuk membuat tampilan daftar yang dapat dimodifikasi secara struktural . Ini berarti bahwa tidak mungkin melakukan operasi yang memengaruhi ukuran daftar. Ini hanya karena ukuran array yang mendasarinya tidak dapat diubah.


Berikut ini adalah MCVE yang menunjukkan opsi implementasi yang berbeda, dan kemungkinan cara menggunakan daftar yang dihasilkan:

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

}

Output dari contoh ditunjukkan di sini:

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
sumber
6

Cara lain dengan Java 8.

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

Saya sedang menulis perpustakaan kecil untuk masalah ini:

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

Jika Anda ingin memeriksanya di sini .

dfa
sumber
perpustakaan yang bagus! pada awalnya, saya tidak yakin itu Java ... Saya suka gaya JQuery
Yanick Rochon
4

Lain cara dengan Java 8.

final long[] a = new long[]{1L, 2L};
final List<Long> l = Arrays.stream(a).boxed().collect(Collectors.toList());
Jin Kwon
sumber
1
Ketika mengembalikan daftar (tidak menugaskannya), saya menemukan saya harus melakukan:Arrays.stream(a).boxed().collect(Collectors.<Long>toList());
Jon
1
Ini identik dengan jawaban yang ada ini .
Pang
3

Menggabungkan jawaban Pavel dan Tom kita dapatkan ini

   @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
sumber
2

Jika Anda ingin semantik serupa Arrays.asListmaka Anda harus menulis (atau menggunakan implementasi orang lain) dari List(mungkin melalui AbstractList. Ini harus memiliki banyak implementasi yang sama seperti Arrays.asList, hanya nilai kotak dan unbox.

Tom Hawtin - tackline
sumber
2

Anda dapat menggunakan transmorph :

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

Ini juga berfungsi jika sumber adalah array int misalnya.

cchabanois
sumber
2

Saya tahu pertanyaan ini sudah cukup lama, tetapi ... Anda juga dapat menulis metode konversi sendiri:

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

Setelah Anda memasukkannya menggunakan impor statis, kemungkinan penggunaan dapat:

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

atau

    List<Long> list = toList(1l, 2l, 3l, 4l, 5l, 6l, 7l, 8l, 9l);
Pavel Netesa
sumber
2
Mengenai:, catch (ArrayIndexOutOfBoundsException ex) { /* Finished getting array elements */ }Anda adalah orang yang mengerikan.
Brandon Yarbrough
Hei, teman, terima kasih untuk itu! Komentar ironis Anda membuat saya menemukan solusi yang lebih baik - mendapatkan panjang array melalui Array.getLength ().
Pavel Netesa
1
Fantastis! Saya senang sikap sinis saya menyebabkan kemajuan, bukan hanya perasaan buruk pada umumnya :) Sungguh, itu bukan ide yang baik untuk menggunakan pengecualian kecuali dalam kondisi yang sangat tidak biasa. Mereka mengejutkan mahal untuk dibuat. Versi baru ini jauh, JAUH lebih cepat.
Brandon Yarbrough
1

Meskipun dimungkinkan untuk membuat Daftar baru dan menambahkan semua nilai ke dalamnya (via for loop atau stream), saya telah bekerja pada array yang sangat besar dan mendapatkan kinerja yang buruk. Oleh karena itu saya membuat kelas pembungkus array primitif saya sendiri yang mudah digunakan.

Contoh:

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

Dapatkan di sini: https://github.com/Sf298/Sauds-Toolbox/blob/master/src/main/java/PrimitiveArrayWrapper/PrimitiveList.java

CATATAN: Saya belum sepenuhnya mengujinya, jadi beri tahu saya jika Anda menemukan bug / masalah.

sf298
sumber
0

Anda bisa menggunakannya LongStreamuntuk itu

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