AFAIK, ada dua pendekatan:
- Iterate di atas salinan koleksi
- Gunakan iterator koleksi aktual
Misalnya,
List<Foo> fooListCopy = new ArrayList<Foo>(fooList);
for(Foo foo : fooListCopy){
// modify actual fooList
}
dan
Iterator<Foo> itr = fooList.iterator();
while(itr.hasNext()){
// modify actual fooList using itr.remove()
}
Adakah alasan untuk lebih menyukai satu pendekatan daripada yang lain (mis. Lebih suka pendekatan pertama karena alasan mudah dibaca)?
java
collections
iteration
pengguna1329572
sumber
sumber
while
memiliki aturan pelingkupan yang berbeda darifor
fooList
variabel instan dan Anda memanggil metode selama loop yang akhirnya memanggil metode lain di kelas yang samafooList.remove(obj)
. Telah melihat ini terjadi. Dalam hal menyalin daftar itu adalah yang paling aman.Jawaban:
Biarkan saya memberi beberapa contoh dengan beberapa alternatif untuk menghindari a
ConcurrentModificationException
.Misalkan kita memiliki koleksi buku berikut
Kumpulkan dan Hapus
Teknik pertama terdiri dari mengumpulkan semua objek yang ingin kita hapus (mis. Menggunakan loop yang disempurnakan) dan setelah kita selesai iterasi, kita menghapus semua objek yang ditemukan.
Seandainya operasi yang ingin Anda lakukan adalah "hapus".
Jika Anda ingin "menambahkan" pendekatan ini juga akan berfungsi, tetapi saya akan menganggap Anda akan mengulangi koleksi yang berbeda untuk menentukan elemen apa yang ingin Anda tambahkan ke koleksi kedua dan kemudian mengeluarkan
addAll
metode di akhir.Menggunakan ListIterator
Jika Anda bekerja dengan daftar, teknik lain terdiri dari penggunaan
ListIterator
yang memiliki dukungan untuk penghapusan dan penambahan item selama iterasi itu sendiri.Sekali lagi, saya menggunakan metode "hapus" dalam contoh di atas yang tampaknya menyiratkan pertanyaan Anda, tetapi Anda juga dapat menggunakan
add
metode ini untuk menambahkan elemen baru selama iterasi.Menggunakan JDK> = 8
Bagi mereka yang bekerja dengan Java 8 atau versi superior, ada beberapa teknik lain yang bisa Anda gunakan untuk memanfaatkannya.
Anda bisa menggunakan
removeIf
metode baru diCollection
kelas dasar:Atau gunakan API aliran baru:
Dalam kasus terakhir ini, untuk memfilter elemen dari koleksi, Anda menetapkan kembali referensi asli ke koleksi yang difilter (yaitu
books = filtered
) atau menggunakan koleksi yang difilter keremoveAll
elemen yang ditemukan dari koleksi asli (yaitubooks.removeAll(filtered)
).Gunakan Sublist atau Subset
Ada alternatif lain juga. Jika daftar diurutkan, dan Anda ingin menghapus elemen berturut-turut Anda dapat membuat sublist dan kemudian menghapusnya:
Karena sublist didukung oleh daftar asli, ini akan menjadi cara yang efisien untuk menghilangkan subkoleksi elemen ini.
Hal serupa dapat dicapai dengan set yang diurutkan menggunakan
NavigableSet.subSet
metode, atau metode pemotongan apa pun yang ditawarkan di sana.Pertimbangan:
Metode apa yang Anda gunakan mungkin tergantung pada apa yang ingin Anda lakukan
removeAl
teknik bekerja dengan Koleksi apa pun (Koleksi, Daftar, Set, dll.).ListIterator
Teknik jelas hanya bekerja dengan daftar, asalkan diberikan merekaListIterator
menawarkan implementasi dukungan untuk add dan menghapus operasi.Iterator
pendekatan akan bekerja dengan semua jenis koleksi, tetapi hanya mendukung operasi menghapus.ListIterator
/Iterator
keuntungan yang jelas adalah tidak perlu menyalin apa pun karena kita menghapus seperti yang kita lakukan berulang kali. Jadi, ini sangat efisien.removeAll
mendekati kerugiannya adalah kita harus mengulang dua kali. Pertama kita mengulang di loop foor mencari objek yang cocok dengan kriteria penghapusan kami, dan setelah kami menemukannya, kami meminta untuk menghapusnya dari koleksi asli, yang akan menyiratkan pekerjaan iterasi kedua untuk mencari item ini untuk Singkirkan.Iterator
antarmuka ditandai sebagai "opsional" di Javadocs, yang berarti bahwa mungkin adaIterator
implementasi yang melemparUnsupportedOperationException
jika kita memanggil metode hapus. Karena itu, saya akan mengatakan pendekatan ini kurang aman dibandingkan yang lain jika kami tidak dapat menjamin dukungan iterator untuk menghilangkan elemen.sumber
removeAll(filtered)
. Jalan pintas untuk itu adalahremoveIf(b -> b.getIsbn().equals(other))
Di Java 8, ada pendekatan lain. Koleksi # removeIf
misalnya:
sumber
Pendekatan pertama akan berhasil, tetapi memiliki overhead yang jelas untuk menyalin daftar.
Pendekatan kedua tidak akan berhasil karena banyak kontainer tidak mengizinkan modifikasi selama iterasi. Ini termasuk
ArrayList
.Jika hanya modifikasi adalah untuk menghapus elemen saat ini, Anda dapat membuat pekerjaan pendekatan kedua dengan menggunakan
itr.remove()
(yaitu, menggunakan iterator 'sremove()
metode, bukan wadah ' s). Ini akan menjadi metode pilihan saya untuk iterator yang mendukungremove()
.sumber
Iterator
antarmuka ditandai sebagai opsional di Javadocs, yang berarti bahwa mungkin ada implementasi Iterator yang mungkin melemparUnsupportedOperationException
. Karena itu, saya akan mengatakan pendekatan ini kurang aman daripada yang pertama. Bergantung pada implementasi yang dimaksudkan untuk digunakan, pendekatan pertama bisa lebih cocok.remove()
pada koleksi aslinya sendiri juga dapat membuangUnsupportedOperationException
: docs.oracle.com/javase/7/docs/api/java/util/… . Antarmuka Java container, sayangnya, didefinisikan sangat tidak dapat diandalkan (mengalahkan titik antarmuka, jujur). Jika Anda tidak tahu implementasi pasti yang akan digunakan saat runtime, lebih baik melakukan hal-hal dengan cara yang tidak dapat diubah - misalnya, gunakan Java 8+ Streams API untuk memfilter elemen ke bawah dan mengumpulkannya ke dalam wadah baru, lalu sepenuhnya ganti yang lama dengan itu.Hanya pendekatan kedua yang akan bekerja. Anda dapat mengubah koleksi selama iterasi
iterator.remove()
hanya menggunakan . Semua upaya lain akan menyebabkanConcurrentModificationException
.sumber
Timer Lama Favorit (masih berfungsi):
sumber
Anda tidak dapat melakukan yang kedua, karena meskipun Anda menggunakan
remove()
metode di Iterator , Anda akan mendapatkan Exception .Secara pribadi, saya lebih suka yang pertama untuk semua
Collection
contoh, meskipun tidak sengaja membuat yang baruCollection
, saya merasa kurang rentan terhadap kesalahan selama mengedit oleh pengembang lain. Pada beberapa implementasi Koleksi, Iteratorremove()
didukung, di lain itu tidak. Anda dapat membaca lebih lanjut di dokumen untuk Iterator .Alternatif ketiga, adalah membuat yang baru
Collection
, beralih dari yang asli, dan tambahkan semua anggota yang pertamaCollection
ke yang keduaCollection
yang tidak bisa dihapus. Tergantung pada ukuranCollection
dan jumlah penghapusan, ini dapat secara signifikan menghemat memori, bila dibandingkan dengan pendekatan pertama.sumber
Saya akan memilih yang kedua karena Anda tidak perlu menyalin memori dan Iterator bekerja lebih cepat. Jadi Anda menghemat memori dan waktu.
sumber
kenapa tidak ini?
Dan jika itu peta, bukan daftar, Anda dapat menggunakan keyset ()
sumber
get(i)
Anda harus mengunjungi semua node sampai Anda mencapaii
.Foo.remove(i);
Anda harus lakukani--;
?