A ConcurrentModificationException elkerülése a Java-ban
1. Bemutatkozás
Ebben a cikkben megnézzük a ConcurrentModificationException osztály.
Először magyarázatot adunk a működésére, majd bebizonyítjuk a kiváltó teszt segítségével.
Végül kipróbálunk néhány megoldást gyakorlati példák segítségével.
2. Kiváltás a ConcurrentModificationException
Lényegében a ConcurrentModificationException régebben kudarcot okoz, ha módosul valami, amit iterálunk. Bizonyítsuk be ezt egy egyszerű teszttel:
A @Test (várható = ConcurrentModificationException.class) public void míg aRemovingDuringIteration_shouldThrowException () dobja az InterruptedException {List integers = newArrayList (1, 2, 3); for (Egész egész szám: egész szám) {integers.remove (1); }}
Mint láthatjuk, az iteráció befejezése előtt eltávolítunk egy elemet. Ez váltja ki a kivételt.
3. Megoldások
Előfordulhat, hogy iterálás közben valóban el akarjuk távolítani az elemeket a gyűjteményből. Ha ez a helyzet, akkor van néhány megoldás.
3.1. Iterátor közvetlen használata
A az egyes hurok egy Iterátor a színfalak mögött, de kevésbé bőbeszédű. Ha azonban az előző tesztünket átalakítottuk egy Iterátor, további módszerekhez férhetünk hozzá, mint pl távolítsa el (). Próbáljuk meg inkább ezt a módszert használni a listánk módosításához:
mert (Iterator iterator = integers.iterator (); iterator.hasNext ();) {Integer integer = iterator.next (); if (egész = = 2) {iterator.remove (); }}
Most észrevesszük, hogy nincs kivétel. Ennek oka, hogy a eltávolítás () módszer nem okoz a ConcurrentModificationException. Iterálás közben biztonságos a telefonálás.
3.2. Nem távolítható el az iteráció során
Ha meg akarjuk tartani a magunkat az egyes hurok, akkor tudjuk. Csak annyit kell várnunk az iteráció után, hogy eltávolítsuk az elemeket. Próbáljuk ki úgy, hogy hozzáadjuk az eltávolítandó elemeket az a-hoz eltávolítani listázzuk iterálás közben:
Lista egészek = newArrayList (1, 2, 3); List toRemove = newArrayList (); for (Egész egész szám: egész szám) {if (egész = = 2) {toRemove.add (egész); }} integers.removeAll (toRemove); assertThat (egész számok) .conactExactly (1, 3);
Ez egy másik hatékony módszer a probléma kiküszöbölésére.
3.3. Használata removeIf ()
A Java 8 bemutatta a removeIf () módszer a Gyűjtemény felület. Ez azt jelenti, hogy ha dolgozunk vele, akkor a funkcionális programozás ötleteit felhasználhatjuk ugyanazok az eredmények elérésére:
Lista egészek = newArrayList (1, 2, 3); egészek.removeIf (i -> i == 2); assertThat (egész számok) .conactExactly (1, 3);
Ez a kijelentő stílus kínál számunkra a legkevésbé bőbeszédet. A felhasználási esettől függően azonban más módszereket találhatunk kényelmesebbnek.
3.4. Szűrés adatfolyamok használatával
Ha belemerülünk a funkcionális / deklaratív programozás világába, megfeledkezhetünk a gyűjtemények mutációjáról, ehelyett azokra az elemekre összpontosíthatunk, amelyeket valóban feldolgozni kellene:
Gyűjtemény egész számai = newArrayList (1, 2, 3); Összegyűjtött lista = egész számok .stream () .filter (i -> i! = 2) .map (Object :: toString) .collect (toList ()); assertThat (összegyűjtött) .conactExactly ("1", "3");
Megtettük az előző példánk inverzét, és állítmányt adtunk az elemek meghatározására, amelyek tartalmazzák, nem pedig kizárják. Előnye, hogy az eltávolítás mellett más funkciókat is összekapcsolhatunk. A példában egy függvényt használunk térkép(), de még több műveletet is használhat, ha akarjuk.
4. Következtetés
Ebben a cikkben bemutattuk azokat a problémákat, amelyekkel szembesülhet, ha elemeket távolít el a gyűjteményből az ismétlés közben, és néhány megoldást is nyújtottunk a probléma elvetésére.
Ezeknek a példáknak a megvalósítása megtalálható a GitHub oldalon. Ez egy Maven projekt, ezért könnyen futtathatónak kell lennie.