Cara termudah untuk mengubah koleksi menjadi array?

125

Misalkan kita punya Collection<Foo>. Apa cara terbaik (terpendek dalam LoC dalam konteks saat ini) untuk mengubahnya Foo[]? Setiap perpustakaan terkenal diizinkan.

UPD: (satu kasus lagi di bagian ini; tinggalkan komentar jika Anda pikir layak membuat utas lain untuk itu): Bagaimana dengan mentransformasikan Collection<Foo>ke Bar[]mana Barmemiliki konstruktor dengan 1 parameter tipe Fooyaitu public Bar(Foo foo){ ... }?

Roma
sumber
Membuat jawaban ini dengan API alternatif yang diperkenalkan di JDK-11 untuk melakukan operasi yang sama dengan kinerja yang sama dan penjelasannya. Plus sintaksinya cocok dengan Stream.toArrayAPI yang ada dari JDK.
Naman

Jawaban:

256

Di mana xkoleksinya:

Foo[] foos = x.toArray(new Foo[x.size()]);
doublep
sumber
35
lebih sederhana (lebih mudah) tetapi tidak terbaik (memori): x.toArray(new Foo[0]) --- dokumentasi : tidak ada waktu untuk membaca ...
user85421
6
@Carlos sebenarnya, ia menggunakan memori yang lebih baik daripada (new Foo[0]). Sesuai dengan dokumentasi Collection.toArray, `@param sebuah larik di mana elemen-elemen koleksi ini disimpan, jika itu cukup besar` yang artinya akan menyimpannya langsung di larik baru itu. Jika Anda memberinya ukuran array 0, itu akan membuat array baru, yang berarti Anda memiliki array kecil dan array besar dibuat ketika itu tidak perlu.
corsiKa
4
@ glowcoder - tetapi ini dapat diminimalkan jika Anda mendefinisikan array kosong satu kali sebagai konstanta statis. Ini hanya petunjuk untuk toArraymembuat array target dari tipe yang benar. Dan dalam hal ini, jujur, saya tidak akan peduli tentang satu array kosong tambahan dalam aplikasi saya. Itu jauh di bawah tingkat kebisingan.
Andreas Dolk
2
@ glowcoder - itulah sebabnya saya ( mencoba ) menulis bahwa menggunakan new Foo[0]lebih sederhana "tetapi tidak terbaik (memori)" ... yaitu, saya maksudkan bahwa solusi saya lebih mudah tetapi tidak terbaik (itulah alasan saya menggunakan :).
user85421
21
Perhatikan bahwa kode ini bukan threadsafe, bahkan jika Anda menggunakan koleksi yang disinkronkan. Ini akan berperilaku sendiri di sebagian besar situasi, tetapi jika suatu item dihapus antara panggilan ke x.size () dan x.toArray (), array yang dihasilkan akan berisi elemen nol tambahan di akhir. The new Foo[0]varian diusulkan oleh Carlos tidak menderita masalah ini.
Jules
36

Solusi alternatif untuk pertanyaan yang diperbarui menggunakan Java 8:

Bar[] result = foos.stream()
    .map(x -> new Bar(x))
    .toArray(size -> new Bar[size]);
Jules
sumber
35
Ini dapat disingkat menjadi Bar[] result = foos.stream().map(Bar::new).toArray(Bar[]::new);
lpandzic
Sintaks bijak saya lebih suka jawaban yang diterima. Saya berharap semua orang bisa beralih ke Python.
Eric Chen
@EricChen Beralih ke Python - bahasa yang ditafsirkan dan diketik secara dinamis dari Jawa - 'dikompilasi dan diketik secara statis' karena pengkodean sintaksis dan garis bilangan berbeda? Bukan ide yang bagus.
RafiAlhamd
10

Jika Anda menggunakannya lebih dari sekali atau dalam satu lingkaran, Anda bisa menentukan konstanta

public static final Foo[] FOO = new Foo[]{};

dan lakukan konversi seperti itu

Foo[] foos = fooCollection.toArray(FOO);

The toArrayMetode akan mengambil array kosong untuk menentukan jenis yang tepat dari array target dan membuat array baru untuk Anda.


Inilah proposal saya untuk pembaruan:

Collection<Foo> foos = new ArrayList<Foo>();
Collection<Bar> temp = new ArrayList<Bar>();
for (Foo foo:foos) 
    temp.add(new Bar(foo));
Bar[] bars = temp.toArray(new Bar[]{});
Andreas Dolk
sumber
1
ups, constbukan Java! public static final Foo[] FOO = {}
user85421
1
mengapa itu harus umum?
Zoltán
@ Zoltán - mengapa tidak? Itu tidak berubah, jadi tidak menghadirkan masalah keamanan. Ini sedikit berantakan, tetapi bisa berguna dalam kode lain, jadi umumnya tidak berbahaya. Mungkin bahkan membuat kelas sentral dengan semua konstanta ini, dan kemudian gunakan import staticuntuk mendapatkannya.
Jules
@ Jules referensi ke array tidak berubah, tetapi isinya tidak. Siapa pun di luar kelas dapat mengganti elemen-elemen array dengan bebas. Dan di sisi lain, sebagai patokan, saya percaya semua bidang harus dijadikan pribadi sampai dibutuhkan di luar kelas.
Zoltán
2
Menjadi nol panjang, tidak ada konten yang dapat diubah
Jules
5

Dengan JDK / 11, cara alternatif untuk mengonversi a Collection<Foo>menjadi Foo[]dapat digunakan Collection.toArray(IntFunction<T[]> generator)sebagai:

Foo[] foos = fooCollection.toArray(new Foo[0]); // before JDK 11
Foo[] updatedFoos = fooCollection.toArray(Foo[]::new); // after JDK 11

Sebagaimana dijelaskan oleh @Stuart pada milis (penekanan milik saya), kinerja ini pada dasarnya harus sama dengan yang ada Collection.toArray(new T[0])-

Hasilnya adalah implementasi yang menggunakan Arrays.copyOf() adalah yang tercepat, mungkin karena itu adalah intrinsik .

Ini dapat menghindari nol-mengisi array yang baru dialokasikan karena tahu seluruh isi array akan ditimpa. Ini benar terlepas dari seperti apa API publik itu.

Implementasi API dalam JDK berbunyi:

default <T> T[] toArray(IntFunction<T[]> generator) {
    return toArray(generator.apply(0));
}

Panggilan implementasi standar generator.apply(0)untuk mendapatkan array nol panjang dan kemudian hanya memanggil toArray(T[]). Ini melewati Arrays.copyOf() jalur cepat, jadi pada dasarnya kecepatannya sama dengan toArray(new T[0]).


Catatan : - Hanya saja penggunaan API akan dipandu bersama dengan ketidakcocokan ke belakang ketika digunakan untuk kode dengannullnilai-nilai misalnyatoArray(null)karena panggilan ini sekarang akan ambigu karena adatoArray(T[] a)dan akan gagal untuk dikompilasi.

Naman
sumber
4

Jika Anda menggunakan Jambu dalam proyek Anda, Anda dapat menggunakan Iterables::toArray.

Foo[] foos = Iterables.toArray(x, Foo.class);
Andrea Bergonzo
sumber
3

Untuk yang asli, lihat jawaban doublep :

Foo[] a = x.toArray(new Foo[x.size()]);

Adapun pembaruan:

int i = 0;
Bar[] bars = new Bar[fooCollection.size()];
for( Foo foo : fooCollection ) { // where fooCollection is Collection<Foo>
    bars[i++] = new Bar(foo);
}    
OscarRyz
sumber
3

Inilah solusi terakhir untuk kasing di bagian pembaruan (dengan bantuan Koleksi Google):

Collections2.transform (fooCollection, new Function<Foo, Bar>() {
    public Bar apply (Foo foo) {
        return new Bar (foo);
    }
}).toArray (new Bar[fooCollection.size()]);

Tapi, pendekatan kunci di sini disebutkan dalam jawaban doublep (saya lupa toArraymetode).

Roma
sumber
1
Lihat komentar saya pada jawaban doublep tentang keamanan benang, yang juga berlaku untuk solusi ini. new Bar[0]menghindari masalah.
Jules
-1

Misalnya, Anda memiliki koleksi ArrayList dengan elemen Kelas siswa:

List stuList = new ArrayList();
Student s1 = new Student("Raju");
Student s2 = new Student("Harish");
stuList.add(s1);
stuList.add(s2);
//now you can convert this collection stuList to Array like this
Object[] stuArr = stuList.toArray();           // <----- toArray() function will convert collection to array
Jagadeesh HN
sumber