Vermijd de ConcurrentModificationException in Java

1. Inleiding

In dit artikel zullen we de ConcurrentModificationException klasse.

Eerst zullen we uitleggen hoe het werkt, en het vervolgens bewijzen door een test te gebruiken om het te activeren.

Ten slotte zullen we enkele tijdelijke oplossingen proberen door praktische voorbeelden te gebruiken.

2. Activeren van een ConcurrentModificationException

In wezen is het ConcurrentModificationException is gewend aan fail-fast wanneer iets waarop we itereren, wordt gewijzigd. Laten we dit bewijzen met een eenvoudige test:

@Test (verwacht = ConcurrentModificationException.class) public void whileRemovingDuringIteration_shouldThrowException () gooit InterruptedException {List integers = newArrayList (1, 2, 3); for (Integer integer: integers) {integers.remove (1); }}

Zoals we kunnen zien, verwijderen we voordat we onze iteratie voltooien een element. Dat is wat de uitzondering veroorzaakt.

3. Oplossingen

Soms willen we tijdens het itereren elementen uit een verzameling verwijderen. Als dit het geval is, zijn er enkele oplossingen.

3.1. Direct een iterator gebruiken

EEN voor elk lus gebruikt een Iterator achter de schermen, maar is minder uitgebreid. Als we onze vorige test echter hebben geherstructureerd om een Iterator, we hebben toegang tot aanvullende methoden, zoals verwijderen(). Laten we proberen deze methode te gebruiken om in plaats daarvan onze lijst aan te passen:

for (Iterator iterator = integers.iterator (); iterator.hasNext ();) {Integer integer = iterator.next (); if (integer == 2) {iterator.remove (); }}

Nu zullen we merken dat er geen uitzondering is. De reden hiervoor is dat de verwijderen() methode veroorzaakt geen ConcurrentModificationException. Het is veilig om tijdens het itereren te bellen.

3.2. Niet verwijderen tijdens iteratie

Als we ons willen behouden voor elk loop, dan kunnen we. Het is alleen dat we moeten wachten tot na het itereren voordat we de elementen verwijderen. Laten we dit uitproberen door toe te voegen wat we willen verwijderen aan een verwijderen lijst terwijl we itereren:

Lijst gehele getallen = newArrayList (1, 2, 3); Lijst toRemove = newArrayList (); for (Integer integer: integer) {if (integer == 2) {toRemove.add (integer); }} gehele getallen.removeAll (toRemove); assertThat (gehele getallen) .containsExactly (1, 3); 

Dit is een andere effectieve manier om het probleem te omzeilen.

3.3. Gebruik makend van removeIf ()

Java 8 introduceerde het removeIf () methode naar de Verzameling koppel. Dit betekent dat als we ermee werken, we ideeën over functioneel programmeren kunnen gebruiken om opnieuw dezelfde resultaten te bereiken:

Lijst gehele getallen = newArrayList (1, 2, 3); gehele getallen.removeIf (i -> i == 2); assertThat (gehele getallen) .containsExactly (1, 3);

Deze declaratieve stijl biedt ons de minste hoeveelheid uitgebreidheid. Afhankelijk van het gebruik kunnen we echter andere methoden handiger vinden.

3.4. Filteren met streams

Wanneer we in de wereld van functioneel / declaratief programmeren duiken, kunnen we het muteren van verzamelingen vergeten, in plaats daarvan kunnen we ons concentreren op elementen die daadwerkelijk moeten worden verwerkt:

Verzameling gehele getallen = newArrayList (1, 2, 3); Verzamelde lijst = gehele getallen .stream () .filter (i -> i! = 2) .map (Object :: toString) .collect (toList ()); assertThat (verzameld) .containsExactly ("1", "3");

We hebben het omgekeerde gedaan van ons vorige voorbeeld door een predikaat op te geven voor het bepalen van elementen die moeten worden opgenomen, niet uitgesloten. Het voordeel is dat we naast de verhuizing ook andere functies aan elkaar kunnen koppelen. In het voorbeeld gebruiken we een functioneel kaart(), maar we zouden nog meer operaties kunnen gebruiken als we dat willen.

4. Conclusie

In dit artikel hebben we problemen laten zien die u kunt tegenkomen als u items uit een verzameling verwijdert tijdens het itereren, en we hebben ook enkele oplossingen aangedragen om het probleem op te lossen.

De implementatie van deze voorbeelden is te vinden op GitHub. Dit is een Maven-project, dus het zou gemakkelijk moeten zijn zoals het is.