Undgå ConcurrentModificationException i Java

1. Introduktion

I denne artikel vil vi se på ConcurrentModificationException klasse.

Først vil vi give en forklaring på, hvordan det fungerer, og derefter bevise det ved hjælp af en test til at udløse det.

Endelig prøver vi nogle løsninger ved hjælp af praktiske eksempler.

2. Udløsning a ConcurrentModificationException

I det væsentlige er den ConcurrentModificationException er vant til mislykkes hurtigt, når noget, vi gentager, ændres. Lad os bevise dette med en simpel test:

@Test (forventet = ConcurrentModificationException.class) offentlig ugyldig mens RemovingDuringIteration_shouldThrowException () kaster InterruptedException {List heltal = newArrayList (1, 2, 3); for (Heltals heltal: heltal) {heltal.remove (1); }}

Som vi kan se, fjerner vi et element, før vi er færdige med vores iteration. Det er det, der udløser undtagelsen.

3. Løsninger

Nogle gange vil vi faktisk gerne fjerne elementer fra en samling, mens det gentages. Hvis dette er tilfældet, er der nogle løsninger.

3.1. Brug af en Iterator direkte

EN for hver loop bruger en Iterator bag kulisserne, men er mindre detaljeret. Men hvis vi omformulerede vores tidligere test for at bruge en Iterator, vi får adgang til yderligere metoder, f.eks fjerne(). Lad os prøve at bruge denne metode til at ændre vores liste i stedet:

for (Iterator iterator = heltal.iterator (); iterator.hasNext ();) {Heltal heltal = iterator.next (); hvis (heltal == 2) {iterator.remove (); }}

Nu vil vi bemærke, at der ikke er nogen undtagelse. Årsagen til dette er, at fjerne() metode forårsager ikke a ConcurrentModificationException. Det er sikkert at ringe, mens det gentages.

3.2. Fjerner ikke under itteration

Hvis vi vil beholde vores for hver løkke, så kan vi. Det er bare, at vi skal vente til efter iterering, før vi fjerner elementerne. Lad os prøve dette ved at tilføje det, vi vil fjerne, til en at fjerne liste, når vi gentager:

Vis heltal = newArrayList (1, 2, 3); Liste over toRemove = newArrayList (); for (Heltals heltal: heltal) {if (heltal == 2) {toRemove.add (heltal); }} heltal.removeAll (toRemove); assertThat (heltal) .containsExactly (1, 3); 

Dette er en anden effektiv måde at løse problemet på.

3.3. Ved brug af removeIf ()

Java 8 introducerede removeIf () metode til Kollektion interface. Dette betyder, at hvis vi arbejder med det, kan vi bruge ideer til funktionel programmering til at opnå de samme resultater igen:

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

Denne deklarative stil giver os mindst mulig detaljerethed. Afhængigt af brugen kan vi dog finde andre metoder mere bekvemme.

3.4. Filtrering ved hjælp af streams

Når vi dykker ned i en verden af ​​funktionel / deklarativ programmering, kan vi glemme at mutere samlinger, men i stedet kan vi fokusere på elementer, der faktisk skal behandles:

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

Vi har gjort det omvendte i forhold til vores tidligere eksempel ved at give et predikat til at bestemme elementer, der skal inkluderes, ikke ekskluderes. Fordelen er, at vi kan sammenkæde andre funktioner sammen med fjernelsen. I eksemplet bruger vi en funktionel kort(), men kunne bruge endnu flere operationer, hvis vi ønsker det.

4. Konklusion

I denne artikel har vi vist problemer, som du kan støde på, hvis du fjerner emner fra en samling, mens du gentager det, og vi har også givet nogle løsninger til at afvise problemet.

Implementeringen af ​​disse eksempler findes på GitHub. Dette er et Maven-projekt, så det skal være let at køre som det er.