반복문에서 List 순환 중에 아이템을 안전하게 삭제하는 방법들을 소개합니다.
For Loop를 이용하여 List 순회(Iterating) 중에, 어떤 아이템을 삭제하면 Exception이 발생하거나 어떤 아이템을 탐색하지 못하는 문제가 발생할 수 있습니다.
문제점들을 먼저 알아보고, 해결 방법을 소개합니다.
- 문제 1: ConcurrentModificationException 발생 가능성
- 문제 2: 반복문에서 순회 중, 일부 요소가 탐색에서 누락될 수 있는 가능성
- 해결 방법 1: List의 높은 Index에서 낮은 Index 방향으로 순회
- 해결 방법 2: 탐색 후, 나중에 삭제
- 해결 방법 3: Collection.removeIf()
- 해결 방법 4: Iterator
문제 1: ConcurrentModificationException 발생 가능성
다음과 같이 for loop에서 ArrayList의 특정 아이템을 삭제하게 되면 ConcurrentModificationException가 발생할 수 있습니다. 순회 중에 리스트의 아이템을 삭제했을 때, 다음에 참조하는 아이템의 Index가 List의 범위를 벗어 나게 될 수 있는데, 이 때 Exception이 발생합니다.
다음 코드는 순회 중에 Exception이 발생합니다. 이런 코드는 잠재적인 문제가 있습니다.
import java.util.ArrayList;
import java.util.List;
public class RemoveFromListIterating1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
list.add("melon");
list.add("banana");
for (String item : list) {
if (item.equals("kiwi")) {
list.remove(item);
}
}
}
}
Output:
Exception in thread "main" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:1013)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:967)
at RemoveFromListIterating1.main(RemoveFromListIterating1.java:13)
문제 2: 반복문에서 순회 중, 일부 요소가 탐색에서 누락될 수 있는 가능성
ConcurrentModificationException가 발생하지 않아도, 낮은 Index에서 높은 Index로 탐색하면서 아이템을 삭제하게 되면, 탐색에서 제외되는 아이템이 있을 수 있습니다.
아래 코드를 실행해 보면 리스트 순회 중, kiwi
는 탐색되지 못하였습니다. 따라서, 이런 코드도 잠재적인 문제가 있습니다.
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
list.add("melon");
list.add("banana");
for (int i = 0; i < list.size(); i++) {
String item = list.get(i);
System.out.println("Iterating: " + item);
if (item.equals("apple") || item.equals("kiwi")) {
list.remove(item);
}
}
System.out.println(list);
Output:
Iterating: apple
Iterating: melon
Iterating: banana
[kiwi, melon, banana]
해결 방법 1: List의 높은 Index에서 낮은 Index 방향으로 순회
List의 높은 Index에서 낮은 Index 방향으로 순회하면, for loop에서 아이템을 안전하게 삭제할 수 있습니다. 아이템 삭제가 다음에 탐색할 아이템의 Index에 영향을 주지 않기 때문입니다.
import java.util.ArrayList;
import java.util.List;
public class RemoveFromListIterating2 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
list.add("melon");
list.add("banana");
for (int i = (list.size() - 1); i > -1; i--) {
String item = list.get(i);
System.out.println("Iterating: " + item);
if (item.equals("kiwi")) {
list.remove(item);
}
}
System.out.println("Result: " + list);
}
}
Output:
Iterating: banana
Iterating: melon
Iterating: kiwi
Iterating: apple
Result: [apple, melon, banana]
해결 방법 2: 탐색 후, 나중에 삭제
두개의 for loop를 사용하여 아이템을 삭제하는 방법입니다.
첫번째 Loop에서 삭제할 아이템을 다른 리스트에 저장하고, 두번째 Loop에서 아이템을 안전하게 삭제할 수 있습니다.
import java.util.ArrayList;
import java.util.List;
public class RemoveFromListIterating2_1 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
list.add("melon");
list.add("banana");
List<String> removed = new ArrayList<>();
for (String item : list) {
System.out.println("Iterating: " + item);
if (item.equals("kiwi")) {
removed.add(item);
}
}
for (String item : removed) {
list.remove(item);
}
System.out.println("Result: " + list);
}
}
Output:
Iterating: apple
Iterating: kiwi
Iterating: melon
Iterating: banana
Result: [apple, melon, banana]
해결 방법 3: Collection.removeIf()
Collection.removeIf()
는 인자로 함수형 인터페이스 Predicate를 전달하며, 조건에 맞는 아이템을 안전하게 삭제합니다.
import java.util.ArrayList;
import java.util.List;
public class RemoveFromListIterating3 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
list.add("melon");
list.add("banana");
list.removeIf(item -> item.equals("kiwi"));
System.out.println("Result: " + list);
}
}
Output:
Result: [apple, melon, banana]
해결 방법 4: Iterator
Iterator를 이용하여 리스트를 순회하고, 특정 아이템을 안전하게 삭제할 수 있습니다.
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class RemoveFromListIterating4 {
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("apple");
list.add("kiwi");
list.add("melon");
list.add("banana");
Iterator<String> it = list.iterator();
while (it.hasNext()) {
String item = it.next();
System.out.println("Iterating: " + item);
if (item.equals("kiwi")) {
it.remove();
}
}
System.out.println("Result: " + list);
}
}
Output:
Iterating: apple
Iterating: kiwi
Iterating: melon
Iterating: banana
Result: [apple, melon, banana]
Related Posts
- Java - 람다식(Lambda)과 함수형 인터페이스
- Java - 직렬화(Serialize), 역직렬화(Deserialize)
- Java - do while과 while의 차이점
- Java - List empty(null) 체크, 3가지 방법
- Java - ArrayList 초기화, 4가지 방법
- Java - HashSet.addAll()
- Java - HashSet.contains()
- Java - 2차원 배열 선언, 출력 방법
- Java - 리스트 정렬, 3가지 방법
- Java - HashSet 정렬, 3가지 방법
- Java - 리스트 (List, ArrayList)
- Java - 첫 글자만 대문자로 바꾸는 방법
- Java - hashCode(), 사용하는 이유? 구현 방법?
- Java8의 Stream reduce() 사용 방법 및 예제
- Java - filter, map, flatMap 사용 방법 및 예제
- Java - 2개의 Map 합치기 (merge, putAll)
- Java - ConcurrentModificationException 원인 및 해결 방법
- JUnit - @After와 @AfterClass의 차이점
- JUnit - @Before와 @BeforeClass의 차이점
- java와 javac의 차이점
- Java - 자바(JDK) 버전 확인 방법 (터미널, cmd 명령어)
- Java - java.util.Date를 java.sql.Date로 변환
- Java - 시스템 운영체제(OS) 정보 확인
- Java - 코드 실행 시간 측정
- Java - HashSet.retainAll() 사용 방법 및 예제
- Java - ArrayList.retainAll() 사용 방법 및 예제
- Java - ArrayList를 String으로 변환
- Java - float을 int로 변환
- Java - float을 String으로 변환
- Java - String을 boolean으로 변환
- Java - XML을 JSON으로 변환
- Java - ClassNotFoundException 발생 원인 및 해결 방법
- Java - private 생성자를 사용하는 이유
- Java - non-static method cannot be referenced from a static context
- Java - NoSuchMethodError 원인 및 해결 방법