알고리즘/예외처리

[예외 처리] ConcurrentModificationException 예외 관련

moongi 2025. 5. 6. 16:41
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
반응형