Vyhýbanie sa ConcurrentModificationException v Jave

1. Úvod

V tomto článku sa pozrieme na ConcurrentModificationException trieda.

Najskôr vysvetlíme, ako to funguje, a potom to dokážeme testom na jeho spustenie.

Na záver si pomocou praktických príkladov vyskúšame niektoré riešenia.

2. Spustenie a ConcurrentModificationException

V podstate ConcurrentModificationException je zvyknutý rýchle zlyhanie, keď sa upraví niečo, na čom iterujeme. Dokážeme to jednoduchým testom:

@Test (očakáva sa = ConcurrentModificationException.class) public void wh WhileRemovingDuringIteration_shouldThrowException () vyvolá InterruptedException {List integers = newArrayList (1, 2, 3); for (Integer integer: integers) {integers.remove (1); }}

Ako vidíme, pred dokončením našej iterácie odstránime prvok. To je spúšťač výnimky.

3. Riešenia

Niekedy môžeme skutočne chcieť odstrániť prvky zo zbierky pri iterácii. Ak je to tak, potom existuje niekoľko riešení.

3.1. Priame použitie iterátora

A pre každý slučka používa Iterátor v zákulisí, ale je menej verbálny. Ak by sme však refaktorovali náš predchádzajúci test na použitie an Iterátor, budeme mať prístup k ďalším metódam, ako napr odstrániť (). Skúsme namiesto toho použiť túto metódu na úpravu nášho zoznamu:

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

Teraz si všimneme, že neexistuje žiadna výnimka. Dôvodom je to, že odstrániť () metóda nespôsobuje a ConcurrentModificationException. Počas iterácie je bezpečné volať.

3.2. Neodstraňovanie počas iterácie

Ak si chceme zachovať svoje pre každý slučka, potom môžeme. Je to len to, že pred odstránením prvkov musíme počkať až po iterácii. Vyskúšajme to pridaním toho, čo chceme odstrániť do a odobrať zoznam pri iterácii:

Zoznam celých čísel = newArrayList (1, 2, 3); List toRemove = newArrayList (); for (Integer integer: integers) {if (integer == 2) {toRemove.add (integer); }} integers.removeAll (toRemove); assertThat (celé čísla). obsahuje Presne (1, 3); 

Toto je ďalší efektívny spôsob, ako problém obísť.

3.3. Použitím removeIf ()

Java 8 predstavila removeIf () metóda do Zbierka rozhranie. To znamená, že ak s ním pracujeme, môžeme pomocou myšlienok funkčného programovania dosiahnuť znova rovnaké výsledky:

Zoznam celých čísel = newArrayList (1, 2, 3); integers.removeIf (i -> i == 2); assertThat (celé čísla). obsahuje Presne (1, 3);

Tento deklaratívny štýl nám ponúka najmenšiu mieru výrečnosti. V závislosti od prípadu použitia sa nám však môžu zdať pohodlnejšie iné metódy.

3.4. Filtrovanie pomocou streamov

Pri ponorení sa do sveta funkčného / deklaratívneho programovania môžeme zabudnúť na mutovanie zbierok, namiesto toho sa môžeme sústrediť na prvky, ktoré by sa mali skutočne spracovať:

Celé čísla kolekcie = newArrayList (1, 2, 3); Zoznam zhromaždený = celé čísla .stream () .filter (i -> i! = 2) .map (Object :: toString) .collect (toList ()); assertThat (zhromaždené). obsahuje Presne ("1", "3");

Inverziu k nášmu predchádzajúcemu príkladu sme vykonali tak, že sme poskytli predikát na určenie prvkov, ktoré sa majú zahrnúť, nie vylúčiť. Výhodou je, že popri odstránení môžeme spojiť aj ďalšie funkcie. V príklade používame funkčné mapa (), ale mohli by sme použiť ešte viac operácií.

4. Záver

V tomto článku sme si ukázali problémy, s ktorými sa môžete stretnúť, ak odstraňujete položky zo zbierky počas iterácie, a tiež sme poskytli niekoľko riešení, ako tento problém vyvrátiť.

Implementáciu týchto príkladov možno nájsť na GitHub. Toto je projekt Maven, takže by mal byť ľahko spustiteľný tak, ako je.


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