728x90
반응형
❗해당 예외가 발생하는 원인
-> iterator로 순회하면서 동시에 add()를 수행했기 때문이다.
HashSet의 iterator()는 fail-fast 구조이다.
즉, 반복 중에 Set의 구조가 변경(add/remove) 되면 예외가 발생한다.
🔍 예시 상황
Iterator<Integer> iterator = weight.iterator();
while (iterator.hasNext()) {
Integer w = iterator.next();
weight.add(w + arr[i]); // ❗ 여기서 ConcurrentModificationException 발생
weight.add(Math.abs(w - arr[i])); // ❗ 동일
}
✅ fail-fast란?
Java의 컬렉션 프레임워크에서 반복 중 구조가 변경되면 즉시 예외를 발생시키는 동작 방식을 의미한다.
🔍 예시 상황
List<Integer> list = new ArrayList<>();
list.add(1); list.add(2); list.add(3);
for (Integer i : list) {
list.add(4); // ❌ ConcurrentModificationException 발생
}
- 리스트를 순회하면서 구조(list의 크기)를 바꾸기 때문에 ConcurrentModificationException이 발생한다.
🔍 fail-fast 의 자료구조
Java에서는 동시성(멀티스레드 환경) 을 고려한 컬렉션에서는 fail-safe 구조를 제공
=> 구조 변경이 감지되어도 예외를 던지지 않고, 복사본을 순회하거나, 변경을 반영하지 않음으로써 문제를 방지
컬렉션 타입 | 구현체 |
List | ArrayList, LinkedList |
Set | HashSet, LinkedHashSet, TreeSet |
Map | HashMap, LinkedHashMap, TreeMap (keySet/entrySet 순회 시) |
Queue | ArrayDeque, LinkedList 등 |
🔍 fail-safe 의 자료구조
컬렉션 | 특성 |
CopyOnWriteArrayList | 반복 중에 구조 변경 가능 (복사본 기반 순회) |
ConcurrentHashMap | 내부 구조 변경에도 안전하게 순회 가능 |
✅ 해결 방법
반복 중에 원래 weight에 직접 추가하지 말고,
임시 리스트에 먼저 추가할 값을 모아서 반복이 끝난 뒤에 추가한다.
🔍 예시 상황
for (int i = 0; i < N; i++) {
Set<Integer> next = new HashSet<>();
for (Integer w : weight) {
next.add(w + arr[i]);
next.add(Math.abs(w - arr[i]));
}
next.add(arr[i]); // 자기 자신도 가능한 무게
weight.addAll(next); // 한꺼번에 추가
}
728x90
반응형