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.maixuanviet.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.maixuanviet.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.maixuanviet.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);
    }
}
&#91;/code&#93;
<!-- /wp:shortcode -->

<!-- wp:paragraph -->
<p>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&nbsp;<strong>IndexOutOfBoundsException</strong>.</p>
<!-- /wp:paragraph -->

<!-- wp:paragraph -->
<p>Ví dụ:</p>
<!-- /wp:paragraph -->

<!-- wp:shortcode -->

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
}
&#91;/code&#93;
<!-- /wp:shortcode -->

<!-- wp:paragraph -->
<p>Output của chương trình:</p>
<!-- /wp:paragraph -->

<!-- wp:shortcode -->

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.maixuanviet.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 -&gt; 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.

Related posts:

Handling Errors in Spring WebFlux
Java Program to Sort an Array of 10 Elements Using Heap Sort Algorithm
HttpClient Basic Authentication
Java Program to Implement Wagner and Fisher Algorithm for online String Matching
The Dining Philosophers Problem in Java
Assert an Exception is Thrown in JUnit 4 and 5
Jackson Annotation Examples
Check If a String Is Numeric in Java
Exception Handling in Java
Java Program to Implement Best-First Search
Java Program to Generate Date Between Given Range
Java Program to Create a Random Graph Using Random Edge Generation
Java Program to Perform Complex Number Multiplication
Java Program to Implement Park-Miller Random Number Generation Algorithm
REST Web service: Upload và Download file với Jersey 2.x
Java Program to Generate Random Partition out of a Given Set of Numbers or Characters
Hướng dẫn Java Design Pattern – Composite
Java Program to do a Breadth First Search/Traversal on a graph non-recursively
Spring Boot - Actuator
Sử dụng Fork/Join Framework với ForkJoinPool trong Java
JWT – Token-based Authentication trong Jersey 2.x
Sắp xếp trong Java 8
Bootstrapping Hibernate 5 with Spring
Spring @Primary Annotation
Testing in Spring Boot
Configure a RestTemplate with RestTemplateBuilder
Predicate trong Java 8
Spring Cloud AWS – RDS
Xử lý ngoại lệ đối với trường hợp ghi đè phương thức trong java
Java Program to Find Minimum Element in an Array using Linear Search
DistinctBy in the Java Stream API
Java Program to Perform Naive String Matching