Pertanyaan Konversi ArrayList ke String [] array [duplikat]


Pertanyaan ini sudah memiliki jawaban di sini:

Saya bekerja di lingkungan android dan telah mencoba kode berikut, tetapi tampaknya tidak berhasil.

String [] stockArr = (String[]) stock_list.toArray();

Jika saya mendefinisikan sebagai berikut:

String [] stockArr = {"hello", "world"};

berhasil. Apakah ada sesuatu yang saya lewatkan?


903
2018-03-21 05:57


asal


Jawaban:


Gunakan seperti ini.

List<String> stockList = new ArrayList<String>();
stockList.add("stock1");
stockList.add("stock2");

String[] stockArr = new String[stockList.size()];
stockArr = stockList.toArray(stockArr);

for(String s : stockArr)
    System.out.println(s);

1504
2018-03-21 06:07



Coba ini

String[] arr = list.toArray(new String[list.size()]);

818
2018-03-21 06:03



Apa yang terjadi adalah itu stock_list.toArray() sedang membuat Object[] bukannya a String[] dan karenanya typecast gagal1.

Kode yang benar adalah:

  String [] stockArr = stockList.toArray(new String[stockList.size()]);

atau bahkan

  String [] stockArr = stockList.toArray(new String[0]);

(Anehnya, versi terakhir lebih cepat dalam rilis Java baru-baru ini: lihat https://stackoverflow.com/a/4042464/139985)

Untuk lebih jelasnya, lihat javadocs untuk dua kelebihan dari List.toArray.

(Dari perspektif teknis, alasan untuk perilaku / desain API ini adalah bahwa implementasi dari List<T>.toArray() metode tidak memiliki informasi tentang apa itu <T> saat runtime. Yang dia tahu adalah tipe elemen mentahnya Object. Sebaliknya, dalam kasus lain, parameter array memberikan tipe dasar dari array. (Jika array yang disediakan cukup besar, digunakan. Jika tidak, array baru dengan tipe yang sama dan ukuran yang lebih besar akan dialokasikan dan dikembalikan sebagai hasilnya.)


1 - Di Jawa, sebuah Object[] bukan tugas yang kompatibel dengan String[]. Jika ya, maka Anda bisa melakukan ini:

    Object[] objects = new Object[]{new Cat("fluffy")};
    Dog[] dogs = (Dog[]) objects;
    Dog d = dogs[0];     // Huh???

Ini jelas tidak masuk akal, dan itulah mengapa tipe array umumnya tidak cocok dengan tugas.


254
2018-03-21 06:05



Alternatif di Java 8:

String[] strings = list.stream().toArray(String[]::new);

114
2018-04-20 01:58



Saya dapat melihat banyak jawaban yang menunjukkan bagaimana memecahkan masalah, tetapi hanya Jawaban Stephen sedang mencoba menjelaskan mengapa masalah terjadi jadi saya akan mencoba menambahkan sesuatu lebih banyak pada subjek ini. Ini adalah kisah tentang kemungkinan alasan mengapa Object[] toArray tidak berubah menjadi T[] toArray di mana obat-obatan generik diperkenalkan ke Jawa.


Mengapa String[] stockArr = (String[]) stock_list.toArray(); tidak akan bekerja?

Di Jawa, tipe generik hanya ada pada waktu kompilasi. Saat informasi runtime tentang tipe generik (seperti dalam kasus Anda <String>) dihapus dan diganti dengan Object ketik (lihatlah ketik penghapusan). Itu sebabnya saat runtime toArray() tidak tahu tentang jenis tepat apa yang digunakan untuk membuat array baru, sehingga menggunakan Object sebagai jenis yang paling aman, karena setiap kelas memperluas Objek sehingga dapat menyimpan instance kelas apa pun dengan aman.

Sekarang masalahnya adalah Anda tidak dapat mentransmisikan instance Object[] untuk String[].

Mengapa? Lihatlah contoh ini (mari kita asumsikan itu class B extends A):

//B extends A
A a = new A();
B b = (B)a;

Meskipun kode tersebut akan dikompilasi, pada saat runtime kita akan melihat dilemparkan ClassCastException karena misalnya dipegang oleh referensi a sebenarnya bukan tipe B (atau subtipe-nya). Mengapa masalah ini (mengapa pengecualian ini perlu dilemparkan)? Salah satu alasannya adalah itu B bisa memiliki metode / bidang baru yang A tidak, jadi mungkin seseorang akan mencoba menggunakan anggota baru ini melalui b referensi bahkan jika diadakan contoh tidak memiliki (tidak mendukung) mereka. Dengan kata lain kita bisa mencoba menggunakan data yang tidak ada, yang bisa menimbulkan banyak masalah. Jadi untuk mencegah situasi seperti itu, JVM melempar pengecualian, dan menghentikan lebih lanjut kode yang berpotensi berbahaya.

Anda bisa bertanya sekarang "Jadi mengapa kita tidak berhenti lebih awal? Mengapa kode yang melibatkan casting seperti itu bahkan dapat dikompilasi? Bukankah seharusnya kompilator menghentikannya?". Jawabannya adalah: tidak karena compiler tidak dapat mengetahui dengan pasti jenis aktual dari apa yang dipegang oleh a referensi, dan ada kemungkinan bahwa itu akan mengadakan turunan kelas B yang akan mendukung antarmuka b referensi. Lihatlah contoh ini:

A a = new B(); 
      //  ^------ Here reference "a" holds instance of type B
B b = (B)a;    // so now casting is safe, now JVM is sure that `b` reference can 
               // safely access all members of B class

Sekarang mari kembali ke array Anda. Seperti yang Anda lihat, kami tidak dapat mentransmisikan instance Object[] array ke tipe yang lebih tepat String[] seperti

Object[] arr = new Object[] { "ab", "cd" };
String[] arr2 = (String[]) arr;//ClassCastException will be thrown

Masalah di sini sedikit berbeda. Sekarang kami yakin itu String[] larik tidak akan memiliki bidang atau metode tambahan karena setiap dukungan larik saja:

  • [] operator,
  • length diajukan,
  • metode yang diwarisi dari Supertype Objek,

Begitulah tidak antarmuka array yang membuatnya tidak mungkin. Masalahnya adalah itu Object[] Array samping Strings dapat menyimpan benda apa saja (contohnya Integers) jadi ada kemungkinan bahwa suatu hari yang indah kita akan berakhir dengan mencoba untuk memanggil metode seperti strArray[i].substring(1,3) pada contoh Integer yang tidak memiliki metode seperti itu.

Jadi untuk membuatnya yakin bahwa situasi ini akan terjadi tak pernah terjadi, dalam referensi array Java hanya dapat memegang

  • contoh array tipe yang sama sebagai referensi (referensi String[] strArr dapat memegang String[])
  • contoh array subtipe (Object[] dapat memegang String[] karena String adalah subtipe dari Object),

tetapi tidak bisa menahan

  • array supertype dari jenis array dari referensi (String[] tidak bisa menahan Object[])
  • larik jenis yang tidak terkait dengan tipe dari referensi (Integer[] tidak bisa menahan String[])

Dengan kata lain, hal seperti ini tidak apa-apa

Object[] arr = new String[] { "ab", "cd" }; //OK - because
               //  ^^^^^^^^                  `arr` holds array of subtype of Object (String)
String[] arr2 = (String[]) arr; //OK - `arr2` reference will hold same array of same type as 
                                //     reference

Anda dapat mengatakan bahwa salah satu cara untuk menyelesaikan masalah ini adalah dengan menemukan pada saat runtime tipe yang paling umum di antara semua elemen daftar dan membuat larik jenis itu, tetapi ini tidak akan berfungsi dalam situasi di mana semua elemen daftar akan menjadi satu jenis yang berasal dari generik. Lihatlah

//B extends A
List<A> elements = new ArrayList<A>();
elements.add(new B());
elements.add(new B());

sekarang tipe yang paling umum adalah Btidak A begitu toArray() 

A[] arr = elements.toArray();

akan mengembalikan array B kelas new B[]. Masalah dengan array ini adalah bahwa ketika kompiler akan memungkinkan Anda untuk mengedit kontennya dengan menambahkan new A() elemen untuk itu, Anda akan dapatkan ArrayStoreException karena B[] larik hanya dapat menampung elemen kelas B atau subkelasnya, untuk memastikan bahwa semua elemen akan mendukung antarmuka B, tetapi contoh A mungkin tidak memiliki semua metode / bidang B. Jadi solusi ini tidak sempurna.


Solusi terbaik untuk masalah ini secara eksplisit memberi tahu jenis array apa toArray() harus dikembalikan dengan meneruskan jenis ini sebagai argumen metode seperti

String[] arr = list.toArray(new String[list.size()]);

atau

String[] arr = list.toArray(new String[0]); //if size of array is smaller then list it will be automatically adjusted.

81
2017-07-28 14:19



Cara yang benar untuk melakukan ini adalah:

String[] stockArr = stock_list.toArray(new String[stock_list.size()]);

Saya ingin menambahkan jawaban hebat lainnya di sini dan menjelaskan bagaimana Anda bisa menggunakan Javadocs untuk menjawab pertanyaan Anda.

Javadoc untuk toArray() (tidak ada argumen) sini. Seperti yang Anda lihat, metode ini mengembalikan sebuah Object[] dan tidak  String[] yang merupakan larik jenis runtime dari daftar Anda:

public Object[] toArray() 

Mengembalikan larik yang berisi semua   elemen dalam koleksi ini. Jika koleksi membuat jaminan apa pun   pada urutan apa elemen-elemennya dikembalikan oleh iteratornya, metode ini   harus mengembalikan elemen dalam urutan yang sama. Array yang dikembalikan akan   "aman" karena tidak ada referensi untuk itu dipelihara oleh koleksi.   (Dengan kata lain, metode ini harus mengalokasikan array baru bahkan jika   koleksi didukung oleh Array). Penelepon dengan demikian bebas untuk dimodifikasi   array yang dikembalikan.

Tepat di bawah metode itu Javadoc untuk toArray(T[] a). Seperti yang Anda lihat, metode ini mengembalikan a T[] dimana T adalah jenis array yang Anda lewati. Pada awalnya ini tampak seperti apa yang Anda cari, tetapi tidak jelas mengapa Anda melewatkan array (apakah Anda menambahkannya, menggunakannya hanya untuk jenis, dll.) . Dokumentasi membuatnya jelas bahwa tujuan dari array yang dilewatkan pada dasarnya adalah untuk menentukan jenis array untuk kembali (yang merupakan kasus penggunaan Anda):

public <T> T[] toArray(T[] a)

Mengembalikan larik yang berisi semua   elemen dalam koleksi ini; jenis runtime dari array yang dikembalikan adalah   bahwa dari array yang ditentukan. Jika koleksi cocok dalam yang ditentukan   array, dikembalikan di dalamnya. Jika tidak, array baru dialokasikan   dengan jenis runtime dari array yang ditentukan dan ukuran ini   koleksi. Jika koleksi cocok dalam array yang ditentukan dengan ruang untuk   cadangan (mis., larik memiliki lebih banyak elemen daripada koleksi),   elemen dalam larik segera setelah akhir koleksi   diatur ke null. Ini berguna dalam menentukan panjang   koleksi hanya jika pemanggil mengetahui bahwa koleksi tidak   mengandung elemen null apa pun.)

Jika koleksi ini membuat jaminan apa urutan elemennya   dikembalikan oleh iteratornya, metode ini harus mengembalikan elemen dalam   urutan yang sama.

Implementasi ini memeriksa apakah array cukup besar untuk memuat   koleksi; jika tidak, ia akan mengalokasikan sebuah array baru dengan ukuran yang benar dan   ketik (menggunakan refleksi). Kemudian, iterates atas koleksi,   menyimpan setiap referensi objek di elemen berurutan berikutnya dari   larik, dimulai dengan elemen 0. Jika larik lebih besar dari   koleksi, nol disimpan di lokasi pertama setelah akhir   koleksi.

Tentu saja, pemahaman tentang generik (seperti yang dijelaskan dalam jawaban lain) diperlukan untuk benar-benar memahami perbedaan antara dua metode ini. Namun demikian, jika Anda pertama kali pergi ke Javadocs, Anda biasanya akan menemukan jawaban Anda dan kemudian melihat sendiri apa lagi yang perlu Anda pelajari (jika Anda benar-benar melakukannya).

Juga perhatikan bahwa membaca Javadocs di sini membantu Anda untuk memahami apa struktur array Anda lulus seharusnya. Meskipun mungkin tidak benar-benar masalah, Anda tidak boleh melewatkan dalam array kosong seperti ini:

String [] stockArr = stockList.toArray(new String[0]);  

Karena, dari doc, implementasi ini memeriksa apakah array cukup besar untuk menampung koleksi; jika tidak, ia mengalokasikan sebuah array baru dengan ukuran dan jenis yang benar (menggunakan refleksi). Tidak perlu tambahan biaya untuk membuat larik baru ketika Anda dapat dengan mudah meneruskan ukurannya.

Seperti biasanya, Javadocs memberi Anda banyak informasi dan arahan.

Hey tunggu sebentar, apa refleksinya?


16
2017-07-28 15:31