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
Iterator
의 remove
메소드를 사용하면, 컬렉션을 순회하면서 안전하게 원소를 삭제할 수 있는 방법이나 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 |