Di masa lalu, saya telah mengatakan untuk menyalin koleksi dengan aman, lakukan sesuatu seperti:
public static void doThing(List<String> strs) {
List<String> newStrs = new ArrayList<>(strs);
atau
public static void doThing(NavigableSet<String> strs) {
NavigableSet<String> newStrs = new TreeSet<>(strs);
Tetapi apakah konstruktor "salin" ini, metode dan aliran penciptaan statis yang serupa, benar - benar aman dan di mana aturan ditentukan? Maksud saya adalah jaminan integritas semantik dasar yang ditawarkan oleh bahasa Jawa dan koleksi yang diberlakukan terhadap penelepon jahat, dengan asumsi didukung oleh alasan yang masuk akal SecurityManager
dan tidak ada kekurangan.
Saya senang dengan metode lempar ConcurrentModificationException
, NullPointerException
, IllegalArgumentException
, ClassCastException
, dll, atau mungkin bahkan menggantung.
Saya telah memilih String
sebagai contoh dari argumen tipe yang tidak dapat diubah. Untuk pertanyaan ini, saya tidak tertarik pada salinan yang dalam untuk koleksi jenis yang bisa berubah yang memiliki gotcha sendiri.
(Untuk lebih jelasnya, saya telah melihat kode sumber OpenJDK dan memiliki beberapa jenis jawaban untuk ArrayList
dan TreeSet
.)
NavigableSet
dan Comparable
koleksi berbasis lainnya kadang-kadang dapat mendeteksi jika kelas tidak menerapkan compareTo()
dengan benar dan melemparkan pengecualian. Agak tidak jelas apa yang Anda maksud dengan argumen yang tidak dipercaya. Maksud Anda seorang penjahat membuat koleksi String buruk dan ketika Anda menyalinnya ke koleksi Anda, sesuatu yang buruk terjadi? Tidak, kerangka koleksi cukup solid, sudah ada sejak 1.2.
HashSet
(dan semua koleksi hashing lainnya secara umum) bergantung pada kebenaran / integritas hashCode
implementasi elemen, TreeSet
dan PriorityQueue
bergantung pada Comparator
(dan Anda bahkan tidak bisa buat salinan yang setara tanpa menerima komparator khusus jika ada), EnumSet
percaya integritas enum
tipe tertentu yang tidak pernah diverifikasi setelah kompilasi, sehingga file kelas, tidak dihasilkan dengan javac
atau buatan tangan, dapat menumbangkannya.
new TreeSet<>(strs)
mana strs
a NavigableSet
. Ini bukan salinan massal, karena hasilnya TreeSet
akan menggunakan komparator sumber, yang bahkan diperlukan untuk mempertahankan semantik. Jika Anda baik-baik saja dengan hanya memproses elemen yang terkandung, toArray()
adalah cara untuk pergi; bahkan akan menjaga urutan iterasi. Ketika Anda baik-baik saja dengan "mengambil elemen, memvalidasi elemen, menggunakan elemen", Anda bahkan tidak perlu membuat salinan. Masalah dimulai ketika Anda ingin memverifikasi semua elemen, diikuti dengan menggunakan semua elemen. Kemudian, Anda tidak dapat mempercayai TreeSet
pembanding khusus salinan
checkcast
untuk setiap elemen, adalah toArray
dengan tipe tertentu. Kami selalu mengakhirinya. Koleksi generik bahkan tidak tahu jenis elemen mereka yang sebenarnya, jadi pembuat salinan mereka tidak dapat memberikan fungsionalitas yang serupa. Tentu saja, Anda dapat menunda pemeriksaan apa pun untuk penggunaan yang benar sebelumnya, tetapi kemudian, saya tidak tahu apa tujuan pertanyaan Anda. Anda tidak perlu "integritas semantik", ketika Anda baik-baik saja dengan memeriksa dan gagal segera sebelum menggunakan elemen.