Fail-fast- ja fail-safe-iteraattorit
Ymmärrä ConcurrentModificationException-poikkeus ja fail-fast-toiminta sekä tiedä, milloin snapshot-iteraattoreita kannattaa käyttää.
Fail-fast- ja fail-safe-iteraattorit on ilmainen Java Academy-oppitunti CoddyKitissä. Tämä on oppitunti 4/4. Voit lukea koko oppitunnin alta ilmaiseksi ja harjoitella sen jälkeen käytännössä selaimessa sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla. Oppitunti kuuluu Java Academy-oppimispolkuun, ja edistymisesi synkronoituu verkon ja CoddyKit-sovelluksen välillä. Java Academy-kurssilla on yhteensä 4 oppituntia.
Fail-fast- ja fail-safe-iteraattorit
Java-iteraattorit luokitellaan niiden toiminnan perusteella tilanteessa, jossa taustalla olevaa kokoelmaa muokataan läpikäynnin aikana: fail-fast-iteraattorit ilmoittavat virheestä heti, kun taas fail-safe-iteraattorit jatkavat toimintaansa tilannevedoksen perusteella.
Fail-fast-iteraattorit
Useimmat Javan vakiokokoelmat (ArrayList, HashMap, TreeSet) käyttävät fail-fast-iteraattoreita. Ne seuraavat modCount-arvoa eli muutoslaskuria. Rakenteellinen muutos läpikäynnin aikana aiheuttaa ConcurrentModificationException-poikkeuksen.
import java.util.*;
List<String> list = new ArrayList<>(List.of("a", "b", "c"));
try {
for (String s : list) {
list.add("x"); // structural modification — throws!
}
} catch (ConcurrentModificationException e) {
System.out.println("ConcurrentModificationException caught!");
}
// Same for HashMap, TreeMap, HashSet, etc.Miksi fail-fast on olemassa
Fail-fast-toiminta auttaa virheenkorjauksessa: se paljastaa virheet heti sen sijaan, että läpikäynti jatkuisi vioittuneen tilan kanssa. Vaihtoehtona oleva hiljainen tietojen vioittuminen olisi huomattavasti vaikeampi korjata.
// modCount is incremented on every structural change
// (add, remove, clear on ArrayList)
// Iterator records modCount on creation
// On each next(), Iterator checks: if current modCount != expected, throw CME
// This detects bugs like:
List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5));
for (Integer n : nums) {
if (n == 3) nums.remove(n); // bug caught immediately
}
// ConcurrentModificationException — not silent wrong resultsTurvallinen poistaminen: Iterator.remove()
Ainoa turvallinen muutos Iterator-pohjaisen läpikäynnin aikana on Iterator.remove(): se poistaa viimeksi palautetun alkion ja päivittää modCount-arvon.
List<Integer> nums = new ArrayList<>(List.of(1,2,3,4,5,6));
Iterator<Integer> it = nums.iterator();
while (it.hasNext()) {
int n = it.next();
if (n % 2 == 0) it.remove(); // safe: updates modCount
}
System.out.println(nums); // [1, 3, 5]removeIf: nykyaikainen vaihtoehto
Java 8 lisäsi Collection-rajapintaan menetelmän removeIf(Predicate), joka tarjoaa selkeämmän tavan ehdon täyttävien alkioiden poistamiseen ilman iteraattorin manuaalista hallintaa.
List<String> names = new ArrayList<>(List.of("Alice", "Bob", "Ann", "Charlie"));
// Modern: removeIf handles iteration internally
names.removeIf(name -> name.startsWith("A"));
System.out.println(names); // [Bob, Charlie]
// Equivalent but verbose iterator approach:
Iterator<String> it = names.iterator();
while (it.hasNext()) {
if (it.next().startsWith("A")) it.remove();
}Fail-safe-iteraattorit: CopyOnWriteArrayList
CopyOnWriteArrayList käyttää fail-safe-iteraattoria. Se käy läpi iteraattorin luomishetkellä otetun tilannevedoksen, joten muutokset eivät vaikuta meneillään olevaan läpikäyntiin.
import java.util.concurrent.CopyOnWriteArrayList;
CopyOnWriteArrayList<String> list = new CopyOnWriteArrayList<>();
list.addAll(List.of("a", "b", "c"));
for (String s : list) {
list.add("x"); // no ConcurrentModificationException!
System.out.print(s + " "); // prints a, b, c (snapshot)
}
System.out.println();
System.out.println(list); // [a, b, c, x, x, x] — modified copyConcurrentHashMap-iteraattori
ConcurrentHashMap käyttää heikosti yhdenmukaisia iteraattoreita: ne kuvaavat tilaa jossakin vaiheessa iteraattorin luomisen aikana tai sen jälkeen. CME-poikkeusta ei heitetä, mutta iteraattori saattaa nähdä samanaikaiset päivitykset tai olla näkemättä niitä.
import java.util.concurrent.ConcurrentHashMap;
ConcurrentHashMap<String, Integer> map = new ConcurrentHashMap<>();
map.put("a", 1); map.put("b", 2); map.put("c", 3);
for (Map.Entry<String, Integer> e : map.entrySet()) {
map.put("d", 4); // no CME — weakly consistent
System.out.println(e.getKey() + "=" + e.getValue());
}
// May or may not print "d=4" — weakly consistentCollections.synchronizedList
Collections.synchronizedList EI ole fail-safe: se käyttää edelleen fail-fast-iteraattoreita, mutta estää samanaikaisen käytön. Teidän on synkronoitava läpikäynti manuaalisesti.
import java.util.*;
List<String> syncList = Collections.synchronizedList(new ArrayList<>());
syncList.addAll(List.of("a", "b", "c"));
// Must synchronize during iteration!
synchronized (syncList) {
for (String s : syncList) {
System.out.println(s);
}
}
// Without the synchronized block, CME is still possible from another threadTilannevedokseen perustuva iteraattorimalli
Luokaa tilannevedos manuaalisesti, jotta voitte käydä muuttuvan kokoelman turvallisesti läpi ilman synkronointia.
import java.util.*;
List<String> original = new ArrayList<>(List.of("a", "b", "c"));
// Take a snapshot copy before iterating
List<String> snapshot = List.copyOf(original);
for (String s : snapshot) {
// Safe to modify original during snapshot iteration
original.remove(s);
System.out.println("Removed: " + s);
}
System.out.println(original); // []CopyOnWrite-rakenteen kompromissit
CopyOnWriteArrayList on fail-safe, mutta kirjoitusoperaatiot ovat kalliita. Jokainen muutos luo täydellisen kopion.
- Sopii: tilanteisiin, joissa luetaan paljon mutta kirjoitetaan harvoin (tapahtumankuuntelijalistat)
- Ei sovi: usein muuttuviin tai suuriin kokoelmiin
Yhteenveto: fail-fast ja fail-safe
Vertailun yhteenveto:
- Fail-fast: ArrayList, HashMap, TreeMap – heittävät CME-poikkeuksen muutoksen yhteydessä ja tarkistavat modCount-arvon
- Fail-safe / heikosti yhdenmukainen: ConcurrentHashMap, CopyOnWriteArrayList – eivät heitä CME-poikkeusta, vaan käyvät läpi tilannevedoksen tai samanaikaisesti muuttuvia tietoja
- Turvallisia muokkausmenetelmiä ovat Iterator.remove(), removeIf() ja replaceAll()
Käytännössä: vanhentuneiden istuntojen siivoaminen
Vanhentuneet istunnot poistetaan turvallisesti samanaikaisesta listasta käyttämällä tarkoitukseen sopivia malleja.
import java.util.*;
import java.util.concurrent.*;
class SessionManager {
private final CopyOnWriteArrayList<Session> sessions = new CopyOnWriteArrayList<>();
record Session(String id, long expiresAt) {
boolean isExpired() { return System.currentTimeMillis() > expiresAt; }
}
void add(Session s) { sessions.add(s); }
// Safe to call from multiple threads while iterating
void purgeExpired() {
sessions.removeIf(Session::isExpired);
}
List<Session> active() {
return sessions.stream().filter(s -> !s.isExpired()).toList();
}
}Pikatarkistus
Minkä poikkeuksen aiheuttaa ArrayList-rakenteen muokkaaminen läpikäynnin aikana?
Kertaus: fail-fast- ja fail-safe-iteraattorit
Tärkeimmät asiat:
- Fail-fast-iteraattorit (ArrayList, HashMap) heittävät ConcurrentModificationException-poikkeuksen samanaikaisen muokkauksen yhteydessä
- Fail-safe-iteraattorit (CopyOnWriteArrayList, ConcurrentHashMap) sallivat muokkaamisen ilman poikkeusta
- Iterator.remove() on ainoa turvallinen tapa poistaa alkio fail-fast-läpikäynnin aikana
- removeIf() on nykyaikainen ja selkeämpi vaihtoehto manuaaliselle Iterator.remove()-kutsulle
- CopyOnWriteArrayList sopii tilanteisiin, joissa luetaan paljon mutta kirjoitetaan harvoin
- ConcurrentHashMap on heikosti yhdenmukainen: se ei heitä CME-poikkeusta, mutta saattaa nähdä samanaikaiset muutokset tai olla näkemättä niitä
Opi Java tekoälytuutorin avulla — ilmaiseksi
Kirjoita ja suorita oikeaa koodia selaimessa, saa välitöntä apua tekoälytuutorilta ympäri vuorokauden ja jatka siitä, mihin jäit, verkossa tai sovelluksessa.
- Kurssit
- 104
- Oppitunnit
- 374
Usein kysytyt kysymykset
Onko oppitunti ”Fail-fast- ja fail-safe-iteraattorit” ilmainen?
Kyllä – oppitunnin ”Fail-fast- ja fail-safe-iteraattorit” koko tekstin voi lukea täällä verkossa ilmaiseksi. Jos haluat harjoitella interaktiivisesti sisäänrakennetulla koodieditorilla ja ympäri vuorokauden käytettävissä olevan tekoälytuutorin avulla sekä avata koko Java Academy-kurssin, päivitä CoddyKit PROhon. Java Academy-kurssilla on yhteensä 4 oppituntia.
Mitä opin oppitunnilla ”Fail-fast- ja fail-safe-iteraattorit”?
Ymmärrä ConcurrentModificationException-poikkeus ja fail-fast-toiminta sekä tiedä, milloin snapshot-iteraattoreita kannattaa käyttää. Harjoittelet Java Academy-aihetta koodilla, jonka suoritat suoraan selaimessa. Ympäri vuorokauden käytettävissä oleva tekoälytuutori vastaa kysymyksiisi oppitunnin aikana.
Tarvitsenko kokemusta aloittaakseni Java Academy-opiskelun?
Aiempi kokemus ei ole tarpeen. CoddyKitin Java Academy-oppimispolku sopii vasta-alkajista edistyneisiin, joten voit aloittaa tästä tai alusta ja edetä omaan tahtiisi. Tämä on oppitunti 4/4.
Kuinka kauan ”Fail-fast- ja fail-safe-iteraattorit”-oppitunnin suorittaminen kestää?
Useimmat CoddyKitin oppitunnit kestävät noin 5–10 minuuttia. Jokainen oppitunti on lyhyt ja interaktiivinen, joten edistyt tasaisesti ja voit jatkaa siitä, mihin jäit – sekä verkossa että sovelluksessa.
Voinko kirjoittaa ja suorittaa koodia tällä Java Academy-oppitunnilla?
Kyllä. Jokainen Java Academy-oppitunti sisältää sisäänrakennetun koodieditorin, joten voit kirjoittaa ja suorittaa oikeaa koodia suoraan selaimessa ja saada välitöntä palautetta tekoälyltä – paikallista asennusta ei tarvita.
Kaikki tämän kurssin oppitunnit
- Iterable- ja Iterator-sopimukset
- Mukautetun iteraattorin toteuttaminen
- ListIterator ja kaksisuuntainen läpikäynti
- Fail-fast- ja fail-safe-iteraattorit