Artikel tersebut ialah terjemahan artikel
" 10 soalan teratas tentang Koleksi Java " . Di bawah ialah soalan paling popular tentang koleksi dalam Java, ditanya dan dibincangkan di Stackowerflow. Sebelum anda melihat soalan ini, adalah baik untuk melihat gambar rajah hierarki kelas.
1. Bila hendak menggunakan LinkedList dan bukannya ArrayList?
ArrayList sebenarnya adalah tatasusunan; unsur-unsurnya boleh diakses secara langsung oleh indeks. Jika tatasusunan melimpah, yang baharu dengan lebih banyak ruang diperlukan. Meletakkan dan memindahkan semua elemen akan mengambil masa O(n). Selain itu, menambah dan mengalih keluar elemen adalah perlu untuk memindahkan elemen sedia ada dalam tatasusunan. Ini mungkin merupakan kesulitan terbesar menggunakan ArrayList. LinkedList ialah senarai berganda pautan elemen. Oleh itu, untuk mengakses elemen di tengah, anda perlu mencari dari awal hingga akhir helaian. Sebaliknya, menambah dan mengalih keluar elemen dalam LinkedList adalah lebih pantas kerana operasi ini hanya mengubah senarai itu sendiri. Masa terburuk dibandingkan di bawah:
Kaedah |
Senarai tatasusunan |
LinkedList |
dapatkan(indeks) |
O(1) |
O(n) |
tambah(E) |
O(n) |
O(1) |
tambah(E, indeks) |
O(n) |
O(n) |
keluarkan(indeks) |
O(n) |
O(n) |
Iterator.remove() |
O(n) |
O(1) |
Iterator.add(E) |
O(n) |
O(1) |
Walaupun masa berjalan, penggunaan memori mesti dipertimbangkan secara individu untuk senarai besar. Dalam LinkedList, setiap nod mesti mempunyai sekurang-kurangnya dua penunjuk tambahan untuk memautkan nod sebelumnya dan seterusnya, manakala dalam ArrayList, hanya tatasusunan elemen diperlukan. Lebih banyak perbandingan
senarai ArrayList, LinkedList dan Vector .
2. Setara yang cekap untuk mengalih keluar elemen semasa lelaran koleksi Satu-satunya cara yang betul untuk mengubah suai (mengalih keluar elemen) koleksi semasa lelaran ialah menggunakan
Iterator.remove() . Contohnya: Ralat yang paling biasa ialah: Anda akan mendapat
ConcurrentModificationException semasa menjalankan kod di atas. Ini berlaku kerana iterator dijana untuk bergerak melalui keseluruhan senarai, tetapi pada masa yang sama helaian ditukar dengan memanggil Iterator.remove(). Seperti yang ditulis dalam dokumentasi untuk pengecualian ini,
Iterator
itr = list.iterator(); while(itr.hasNext()) { // do something itr.remove(); }
for(Integer i: list) { list.remove(i); }
"secara amnya tidak dibenarkan untuk satu utas untuk mengubah suai koleksi manakala satu lagi thread sedang berulang ke atasnya."
Secara umum, satu utas tidak boleh diterima untuk mengubah suai koleksi semasa satu lagi utas melintasinya.
3. Bagaimana untuk menukar Senarai kepada tatasusunan int[]? Cara paling mudah untuk melakukan ini ialah menggunakan
ArrayUtils , yang terletak di perpustakaan
Apache Commons Lang .
int[] array = ArrayUtils.toPrimitive(list.toArray(new Integer[0]));
Tiada jalan pintas untuk ungkapan ini dalam JDK. Ingat bahawa anda tidak boleh menggunakan List.toArray() kerana ungkapan ini menukarkan Senarai kepada Integer[] (yang
bukan jenis primitif). Cara yang betul ialah pilihan berikut:
int[] array = new int[list.size()]; for(int i=0; i < list.size(); i++) { array[i] = list.get(i); }
4. Bagaimana untuk menukar tatasusunan int[] kepada Senarai? Cara paling mudah juga ialah menggunakan
ArrayUtils dalam perpustakaan
Apache Commons Lang , seperti di atas.
List list = Arrays.asList(ArrayUtils.toObject(array));
Juga, tiada jalan pintas untuk ungkapan ini dalam JDK.
5. Apakah cara terbaik untuk menapis koleksi? Anda boleh menggunakan pakej pihak ketiga seperti
Guava atau
Apache Commons Lang untuk meningkatkan fungsi. Kedua-dua pakej ini mempunyai kaedah penapis() (dalam kelas
Collections2 daripada Guava dan
CollectionUtils daripada Apache). Kaedah penapis() akan mengembalikan elemen yang sepadan dengan Predikat yang diberikan. Dalam JDK semuanya lebih rumit. Berita baiknya ialah predikat akan ditambah dalam Java 8
, tetapi buat masa ini anda perlu menggunakan Iterator untuk mengulangi keseluruhan koleksi. Sudah tentu, anda boleh meniru laluan yang diikuti oleh Guava dan Apache dengan membiasakan diri dengan antara muka Predikat baharu. Kini kita boleh menggunakan kod berikut untuk menapis koleksi:
6. Bagaimana untuk menukar Senarai kepada Set dengan mudah? Terdapat dua cara untuk melakukan ini, bergantung pada cara anda ingin mentakrifkan kesaksamaan. Sekeping kod pertama meletakkan senarai ke dalam HashSet. Pendua dalam kes ini ditentukan terutamanya oleh hashCode(). Biasanya ini akan berfungsi. Tetapi jika anda perlu mengambil kira laluan perbandingan, maka lebih baik menggunakan bahagian kedua kod, di mana anda boleh menentukan pembanding anda sendiri.
7. Bagaimanakah saya boleh mengalih keluar elemen pendua daripada ArrayList? Soalan ini agak berkaitan dengan soalan di atas. Jika susunan elemen dalam ArrayList tidak penting kepada anda, langkah bijak ialah meletakkan helaian dalam Set untuk mengalih keluar pendua, dan kemudian mengembalikannya semula ke Senarai. Di bawah adalah contoh. Jika susunan elemen penting kepada anda, maka pesanan itu boleh dipastikan dengan meletakkan senarai dalam
LinkedHashSet , yang terdapat dalam JDK standard.
8. Koleksi yang disusun
int[] array = {1,2,3,4,5}; List
list = new ArrayList
(); for(int i: array) { list.add(i); }
Iterator
itr = list.iterator(); while(itr.hasNext()) { int i = itr.next(); if (i > 5) { // filter all ints bigger than 5 itr.remove(); } }
public interface Predicate
{ boolean test(T o); } public static
void filter(Collection
collection, Predicate
predicate) { if ((collection != null) && (predicate != null)) { Iterator
itr = collection.iterator(); while(itr.hasNext()) { T obj = itr.next(); if (!predicate.test(obj)) { itr.remove(); } } } }
filter(list, new Predicate
() { public boolean test(Integer i) { return i <= 5; } });
Set
set = new HashSet
(list);
Set
set = new TreeSet
(aComparator); set.addAll(list);
ArrayList** list = ... // initial a list with duplicate elements Set
set = new HashSet
(list); list.clear(); list.addAll(set);
Terdapat beberapa cara untuk menyokong koleksi yang diisih dalam Java. Kesemuanya menyediakan koleksi dalam susunan semula jadi atau oleh pembanding tertentu. Dalam kes susunan semula jadi, anda juga perlu melaksanakan antara muka
Sebanding pada elemen.
- Collections.sort() boleh mengisih Senarai. Seperti yang dinyatakan dalam dokumentasi Java, jenis ini adalah stabil dan menjamin prestasi n log(n).
- PriorityQueue menyediakan baris gilir yang teratur. Perbezaan antara PriorityQueue dan Collections.sort() ialah PriorityQueue mengekalkan susunan baris gilir sepanjang masa, tetapi anda hanya boleh mendapatkan elemen pertama baris gilir. Anda tidak boleh mengakses elemen secara rawak seperti PriorityQueue.get(4).
- Jika tiada pendua dalam koleksi, anda boleh memilih TreeSet . Juga seperti PriorityQueue, TreeSet mengekalkan set tersusun pada setiap masa. Anda boleh mendapatkan elemen terkecil atau terbesar daripada TreeSet, tetapi anda masih tidak boleh mempunyai akses rawak kepada elemen.
Ringkasnya, Collections.sort() menyediakan senarai pesanan sekali sahaja. PriorityQueue dan TreeSet mengekalkan koleksi tersusun pada setiap masa, dengan kos kekurangan akses terindeks kepada elemen.
9. Collections.emptyList() atau contoh baharu Soalan yang sama digunakan untuk emptyMap() dan emptySet(). Kedua-dua kaedah mengembalikan senarai kosong, tetapi Collections.emptyList() ialah senarai yang tidak boleh diubah. Ini bermakna anda
tidak boleh menambah elemen baharu pada senarai "kosong". Di latar belakang, setiap panggilan ke kaedah Collections.emptyList() sebenarnya tidak mencipta contoh baharu senarai kosong. Sebaliknya, ia akan menggunakan semula contoh kosong yang sedia ada. Jika anda biasa dengan Singleton sebagai corak
reka bentuk , anda harus memahami apa yang dimaksudkan. Ini sepatutnya memberi anda prestasi
yang lebih baik jika dipanggil dengan kerap.
10 Menyalin koleksi, Collections.copy() Terdapat dua cara untuk menyalin senarai sumber ke senarai destinasi. Satu cara ialah menggunakan pembina ArrayList. Cara lain ialah menggunakan kaedah
Collections.copy() . Perhatikan baris pertama: kami memperuntukkan senarai yang sekurang-kurangnya sepanjang panjang senarai asal, kerana dokumentasi Java tentang koleksi berkata:
ArrayList
dstList = new ArrayList
(srcList);
Senarai destinasi mestilah sekurang-kurangnya sama dengan senarai sumber.
Ini bermakna senarai akhir mestilah tidak lebih pendek daripada yang asal. Kedua-dua kaedah adalah penyalinan cetek. Jadi apakah perbezaan antara kedua-dua kaedah ini? Pertama, Collections.copy() tidak akan mengagihkan semula kapasiti pengumpulan dstList, walaupun dstList tidak mempunyai ruang yang mencukupi untuk mengandungi semua elemen daripada srcList. Sebaliknya, ia akan membuang
IndexOutOfBoundsException . Seseorang mungkin bertanya sama ada terdapat faedah untuk ini. Sebabnya ialah ini memastikan kaedah berjalan secara linear dalam masa. Ini juga sesuai apabila anda ingin menggunakan semula tatasusunan dan bukannya memperuntukkan semula memori dalam pembina ArrayList.
Daripada kesimpulan Jika selepas membaca artikel anda masih mempunyai soalan, jangan ragu untuk bertanya kepada mereka dalam komen. Juga, jika anda mendapati sebarang ketidaktepatan dalam terjemahan atau sebarang kesilapan lain, kemudian tulis kepada PM, ia akan diperbetulkan, dan anda akan berterima kasih.
Asal.
ArrayList
dstList = new ArrayList
(srcList.size()); Collections.copy(dstList, srcList);
GO TO FULL VERSION