1. Pengenalan
Dalam artikel ini, kita akan melihat kelas ConcurrentModificationException .
Pertama, kami akan memberi penjelasan bagaimana ia berfungsi, dan kemudian membuktikannya dengan menggunakan ujian untuk mencetuskannya.
Akhirnya, kami akan mencuba beberapa penyelesaian dengan menggunakan contoh praktikal.
2. Mencetuskan Pengecualian Serentak Pengubahsuaian
Pada dasarnya, ConcurrentModificationException digunakan untuk cepat-cepat apabila sesuatu yang kita lakukan diubah suai. Mari buktikan ini dengan ujian mudah:
@Test(expected = ConcurrentModificationException.class) public void whilstRemovingDuringIteration_shouldThrowException() throws InterruptedException { List integers = newArrayList(1, 2, 3); for (Integer integer : integers) { integers.remove(1); } }
Seperti yang kita lihat, sebelum menyelesaikan lelaran, kita akan membuang elemen. Itulah yang mencetuskan pengecualian.
3. Penyelesaian
Kadang-kadang, kita mungkin mahu membuang elemen dari koleksi semasa melakukan lelaran. Sekiranya ini berlaku, maka ada beberapa penyelesaian.
3.1. Menggunakan Iterator Secara Langsung
A untuk-setiap gelung menggunakan Pelelar belakang tabir tetapi kurang berjela-jela. Walau bagaimanapun, jika kita menguji semula ujian sebelumnya untuk menggunakan Iterator, kita akan mendapat akses ke kaedah tambahan, seperti membuang (). Mari cuba gunakan kaedah ini untuk mengubah senarai kami sebagai gantinya:
for (Iterator iterator = integers.iterator(); iterator.hasNext();) { Integer integer = iterator.next(); if(integer == 2) { iterator.remove(); } }
Sekarang kita akan melihat bahawa tidak ada pengecualian. Sebabnya adalah bahawa kaedah remove () tidak menyebabkan ConcurrentModificationException. Selamat menelefon semasa melakukan lelaran.
3.2. Tidak Mengeluarkan Semasa Pengulangan
Sekiranya kita ingin menjaga setiap gelung kita, maka kita boleh. Kita hanya perlu menunggu sehingga selepas melakukan lelaran sebelum kita membuang unsur-unsurnya. Mari mencuba ini dengan menambahkan perkara yang ingin kami alih keluar ke senarai toRemove semasa kami berulang kali:
List integers = newArrayList(1, 2, 3); List toRemove = newArrayList(); for (Integer integer : integers) { if(integer == 2) { toRemove.add(integer); } } integers.removeAll(toRemove); assertThat(integers).containsExactly(1, 3);
Ini adalah kaedah lain yang berkesan untuk mengatasi masalah ini.
3.3. Menggunakan removeIf ()
Java 8 memperkenalkan kaedah removeIf () ke antara muka Koleksi . Ini bermaksud bahawa jika kita bekerja dengannya, kita dapat menggunakan idea pengaturcaraan fungsional untuk mencapai hasil yang sama sekali lagi:
List integers = newArrayList(1, 2, 3); integers.removeIf(i -> i == 2); assertThat(integers).containsExactly(1, 3);
Gaya perisytiharan ini memberi kita sedikit kata-kata. Namun, bergantung pada kes penggunaan, kami mungkin kaedah lain lebih mudah.
3.4. Menapis Menggunakan Aliran
Semasa menyelami dunia pengaturcaraan fungsional / deklaratif, kita dapat melupakan mutasi koleksi, sebaliknya, kita dapat memusatkan perhatian pada elemen yang sebenarnya harus diproses:
Collection integers = newArrayList(1, 2, 3); List collected = integers .stream() .filter(i -> i != 2) .map(Object::toString) .collect(toList()); assertThat(collected).containsExactly("1", "3");
Kami telah melakukan kebalikan dengan contoh sebelumnya, dengan memberikan predikat untuk menentukan elemen untuk disertakan, tidak dikecualikan. Kelebihannya ialah kita dapat menyatukan fungsi lain bersamaan dengan penyingkiran. Sebagai contoh, kami menggunakan peta fungsional (), tetapi boleh menggunakan lebih banyak operasi jika kami mahu.
4. Kesimpulan
Dalam artikel ini kami telah menunjukkan masalah yang mungkin anda hadapi jika anda membuang item dari koleksi semasa melakukan iterasi, dan juga memberikan beberapa penyelesaian untuk meniadakan masalah tersebut.
Pelaksanaan contoh-contoh ini boleh didapati di GitHub. Ini adalah projek Maven, jadi semestinya mudah dijalankan seperti sedia kala.