[Java] ConcurrentModificationException

ConcurrentModificationException 해결 하기

List에서 특정 조건에 해당하는 원소만 삭제 해야 할 경우가 있다.
특정 숫자를 지워야 해야 한다면 아래와 같은 코드를 떠올릴 수 있을 것이다.

1
2
3
4
5
6
7
8
9
List<Integer> numbers = new ArrayList<>();
//[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
numbers.addAll(Arrays.asList(0,1,2,3,4,5,6,7,8,9));
 
for (Integer number : numbers){
    if(number == 5){
        numbers.remove(number);
    }
}
cs

해당 코드를 실행해보면 ConcurrentModificationException이 발생한다.
1
2
3
4
Exception in thread "main" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:937)
    at java.base/java.util.ArrayList$Itr.next(ArrayList.java:891)
    at Main.main(Main.java:12)
cs

해당 Exception이 IndexOutOfBoundsException이 발생하지 않고, ConcurrentModificationException이 발생하는 이유는 Enhanced for loop에서 Iterator 방식을 사용하여 List를 순회하고 있기 때문이다.

해결 방법

(1) Iterator

Iteratorremove 메소드를 사용하면, 컬렉션을 순회하면서 안전하게 원소를 삭제할 수 있는 방법이나 Multi-Thread 환경에서 성능상의 이슈가 발생할 수 있다.

1
2
3
4
5
6
7
Iterator<Integer> iter = numbers.iterator();
while(iter.hasNext()){
    Integer number = iter.next();
    if(number == 5){
        iter.remove();
    }
}
cs

(2) removeIf

Collection 인터페이스의 removeIf 메소드를 사용할 수 있다.

1
numbers.removeIf(number -> number == 2);
cs

(3) Stream의 filter

자바 8에서 Stream의 filter를 이용하여 원하는 값을 처리하는 방법을 사용할 수 있다.

1
2
3
4
5
6
7
8
List<Integer> temp = number
    .stream()
    .filter(number -> {
        if((number == 5)){
            return false;
        }
        return true;
    }).collect(Collectors.toList());
cs
Share