Unngå ConcurrentModificationException i Java

1. Introduksjon

I denne artikkelen tar vi en titt på ConcurrentModificationException klasse.

Først skal vi forklare hvordan det fungerer, og deretter bevise det ved å bruke en test for å utløse den.

Til slutt vil vi prøve noen løsninger ved å bruke praktiske eksempler.

2. Utløsing a ConcurrentModificationException

I hovedsak er det ConcurrentModificationException er vant til fail-fast når noe vi iterer på blir endret. La oss bevise dette med en enkel test:

@Test (forventet = ConcurrentModificationException.class) offentlig ugyldig mensRemovingDuringIteration_shouldThrowException () kaster InterruptedException {List heltall = newArrayList (1, 2, 3); for (Heltall: heltall) {heltall. fjern (1); }}

Som vi kan se, fjerner vi et element før vi fullfører iterasjonen. Det er det som utløser unntaket.

3. Løsninger

Noen ganger kan det være lurt å fjerne elementer fra en samling mens det gjentas. Hvis dette er tilfelle, er det noen løsninger.

3.1. Bruke en Iterator direkte

EN for hver loop bruker en Iterator bak kulissene, men er mindre ordentlig. Imidlertid, hvis vi refactored vår forrige test for å bruke en Iterator, vi vil ha tilgang til flere metoder, for eksempel fjerne(). La oss prøve å bruke denne metoden for å endre listen vår i stedet:

for (Iterator iterator = heltall.iterator (); iterator.hasNext ();) {Heltall heltall = iterator.next (); hvis (heltall == 2) {iterator.remove (); }}

Nå vil vi merke at det ikke er noe unntak. Årsaken til dette er at fjerne() metoden forårsaker ikke a ConcurrentModificationException. Det er trygt å ringe mens det gjentas.

3.2. Fjerner ikke under iterasjon

Hvis vi vil beholde vår for hver løkke, så kan vi. Det er bare det at vi må vente til etter iterering før vi fjerner elementene. La oss prøve dette ved å legge til det vi vil fjerne til a å fjerne liste når vi gjentar:

Liste heltall = newArrayList (1, 2, 3); List toRemove = newArrayList (); for (Heltall: heltall) {if (heltall == 2) {toRemove.add (heltall); }} heltall.removeAll (toRemove); assertThat (heltall) .containsExactly (1, 3); 

Dette er en annen effektiv måte å komme rundt problemet på.

3.3. Ved hjelp av removeIf ()

Java 8 introduserte removeIf () metoden til Samling grensesnitt. Dette betyr at hvis vi jobber med det, kan vi bruke ideer om funksjonell programmering for å oppnå de samme resultatene igjen:

Liste heltall = newArrayList (1, 2, 3); heltall.removeIf (i -> i == 2); assertThat (heltall) .containsExactly (1, 3);

Denne deklarative stilen gir oss minst mulig ordlighetsgrad. Avhengig av brukssaken kan vi imidlertid finne andre metoder som er mer praktiske.

3.4. Filtrering ved hjelp av strømmer

Når du dykker inn i verden av funksjonell / deklarativ programmering, kan vi glemme å mutere samlinger, i stedet kan vi fokusere på elementer som faktisk skal behandles:

Samling heltall = newArrayList (1, 2, 3); Liste samlet = heltall .stream () .filter (i -> i! = 2) .map (Object :: toString) .collect (toList ()); assertThat (samlet) .containsExactly ("1", "3");

Vi har gjort det motsatte av vårt forrige eksempel, ved å gi et predikat for å bestemme elementene som skal inkluderes, ikke ekskluderes. Fordelen er at vi kan koble sammen andre funksjoner ved siden av fjerningen. I eksemplet bruker vi en funksjonell kart(), men kan bruke enda flere operasjoner hvis vi vil.

4. Konklusjon

I denne artikkelen har vi vist problemer du kan støte på hvis du fjerner gjenstander fra en samling mens du gjentar det, og vi har også gitt noen løsninger for å oppheve problemet.

Implementeringen av disse eksemplene finner du på GitHub. Dette er et Maven-prosjekt, så det skal være enkelt å kjøre det som det er.


$config[zx-auto] not found$config[zx-overlay] not found