Ba mức xử lý "collection bị sửa trong khi đang duyệt":
| Fail-fast | Fail-safe (snapshot) | Weakly-consistent | |
|---|---|---|---|
| Ví dụ | ArrayList, HashMap | CopyOnWriteArrayList | ConcurrentHashMap |
| Behavior | Throw ConcurrentModificationException | Duyệt bản sao, không throw, không thấy thay đổi mới | Không throw, có thể thấy hoặc không thấy thay đổi mới |
| Cost | 0 (kiểm modCount) | Memory + clone khi write | Vừa phải, không clone |
// Fail-fast — ArrayList giữ modCount
List<String> list = new ArrayList<>(List.of("a", "b"));
for (String s : list) list.remove(s); // ❌ CME
// Fail-safe — snapshot
List<String> cow = new CopyOnWriteArrayList<>(List.of("a", "b"));
for (String s : cow) cow.add("c"); // OK — iterator vẫn duyệt snapshot ban đầuFail-fast là best-effort — giúp dev phát hiện bug sớm trong dev.
Cách modify đúng trên fail-fast:
- iterator.remove() — single-thread safe.
- collection.removeIf(predicate) (Java 8+) — gọn nhất.
"Fail-safe" không nghĩa "tốt hơn" — single-thread fail-fast vẫn tốt vì báo lỗi sớm.
Three modes for handling "collection modified during iteration":
| Fail-fast | Fail-safe (snapshot) | Weakly-consistent | |
|---|---|---|---|
| Examples | ArrayList, HashMap | CopyOnWriteArrayList | ConcurrentHashMap |
| Behaviour | Throws ConcurrentModificationException | Iterates a copy, no throw, no new changes | No throw, may or may not reflect new changes |
| Cost | 0 (modCount check) | Memory + clone on writes | Moderate, no clone |
// Fail-fast — ArrayList tracks modCount
List<String> list = new ArrayList<>(List.of("a", "b"));
for (String s : list) list.remove(s); // ❌ CME
// Fail-safe — snapshot
List<String> cow = new CopyOnWriteArrayList<>(List.of("a", "b"));
for (String s : cow) cow.add("c"); // OK — iterator still walks original snapshotFail-fast is best-effort — surfaces bugs early in development.
How to modify a fail-fast collection correctly:
- iterator.remove() — safe in single-threaded code.
- collection.removeIf(predicate) (Java 8+) — cleanest.
"Fail-safe" does not mean "better" — single-threaded fail-fast is preferred because it surfaces bugs early.