NỘI DUNG BÀI VIẾT
Một trong những vấn đề phổ biến trong khi loại bỏ các phần tử từ một ArrayList trong Java là ConcurrentModificationException.
Nếu bạn sử dụng vòng lặp foreach và cố gắng thêm/ xóa phần tử khỏi ArrayList bằng phương thức remove(), bạn sẽ nhận được ConcurrentModificationException.
Tuy nhiên, nếu bạn sử dụng phương thức xóa của Iterator hoặc ListIterator bằng phương thức remove(), bạn sẽ không gặp lỗi này và có thể xóa phần tử đó.
Trong bài viết này, tôi sẽ giải thích, đưa ra một vài ví dụ cho bạn thấy được trường hợp xảy ra lỗi ConcurrentModificationException và cách có thể tránh lỗi này trong khi sửa đổi một ArrayList trong Java.
1. Ví dụ xảy ra lỗi ConcurrentModificationException
1.1. Thêm/xóa phần tử khi sử dụng ArrayList.remove() khi duyệt qua for-each
package com.gpcoder.collection.list.ConcurrentModificationException; import java.util.ArrayList; import java.util.List; public class ConcurrentModificationException1 { public static void main(String[] args) { List<String> languages = new ArrayList<>(); languages.add("Java"); languages.add("C#"); languages.add("PHP"); languages.add("C++"); languages.add("Ruby"); // Using forEach loop to iterate and add/ removing element during iteration will // throw ConcurrentModificationException in Java for (String language : languages) { if (language.equals("C#")) { languages.remove(language); } } } }
Output của chương trình:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at com.gpcoder.collection.list.ConcurrentModificationException.ConcurrentModificationException1.main(ConcurrentModificationException1.java:18)
1.2. Thêm/ xóa phần tử sử dụng ArrayList.remove() khi duyệt qua Iterator
Iterator<String> iterator = languages.iterator(); while (iterator.hasNext()) { String language = iterator.next(); if (language.equals("C#")) { languages.remove(language); } }
Output của chương trình:
Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) at com.gpcoder.collection.list.ConcurrentModificationException.ConcurrentModificationException2.main(ConcurrentModificationException2.java:19)
2. Tránh lỗi ConcurrentModificationException
2.1. Sử dụng vòng lặp for-index
for (int i = 0; i < languages.size(); i++) { String language = languages.get(i); if (language.equals("C#")) { languages.remove(language); } }
Lưu ý: với cách này các bạn nên cẩn thận khi get phần tử theo index, có thể sẽ gặp lỗi IndexOutOfBoundsException.
Ví dụ:
for (int i = 0; i < languages.size(); i++) { String language = languages.get(i); if (language.equals("Ruby")) { languages.remove(language); } System.out.println(languages.get(i)); // IndexOutOfBoundsException }
Output của chương trình:
Java C# PHP C++ Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 4, Size: 4 at java.util.ArrayList.rangeCheck(ArrayList.java:653) at java.util.ArrayList.get(ArrayList.java:429) at com.gpcoder.collection.list.ConcurrentModificationException.ForIndexExample.main(ForIndexExample.java:21)
2.2. Sử dụng phương thức remove() được hỗ trợ bởi Iterator
Iterator<String> iterator = languages.iterator(); while (iterator.hasNext()) { String language = iterator.next(); if (language.equals("C#")) { // languages.remove(language); // Don't use ArrayList.remove() iterator.remove(); } }
2.3. Không remove trong khi duyệt các phần tử
Nếu chúng ta muốn giữ vòng lặp for-each, thì chúng ta cần đợi cho đến khi kết thúc trước khi chúng ta xóa các phần tử.
List<String> toRemove = new ArrayList<>(); for (String language : languages) { if (language.equals("C#") || language.equals("Ruby")) { toRemove.add(language); } } languages.removeAll(toRemove); // [Java, PHP, C++]
2.4. Sử dụng phương thức removeIf() trong Java 8
languages.removeIf(language -> language.equals("C#") || language.equals("Ruby"));
2.5. Sử dụng Stream filter() trong Java 8
List<String> removedList = languages.stream() .filter(language -> language.equals("C#") || language.equals("Ruby")) .collect(Collectors.toList());