Tránh lỗi ConcurrentModificationException trong Java như thế nào?

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());
Trong bài viết này, tôi đã trình bày cho bạn thấy một số trường hợp có thể gặp phải nếu bạn thêm/ xóa một phần tử khi đang duyệt và cũng đã cung cấp một số giải pháp để giải quyết lỗi ConcurrentModificationException. Hy vọng bài viết giúp ích cho các bạn, hẹn gặp lại ở các bài viết tiếp theo.
Nguồn: https://gpcoder.com/4289-tranh-loi-concurrentmodificationexception-trong-java-nhu-the-nao/

Bài viết liên quan

Leave a Reply

Your email address will not be published.