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:

Spring Boot Gradle Plugin
Spring Boot - Service Components
OAuth2 for a Spring REST API – Handle the Refresh Token in Angular
Hashing a Password in Java
The HttpMediaTypeNotAcceptableException in Spring MVC
Java Program to Find the Peak Element of an Array O(n) time (Naive Method)
Spring Cloud – Securing Services
Converting Between Byte Arrays and Hexadecimal Strings in Java
Vấn đề Nhà sản xuất (Producer) – Người tiêu dùng (Consumer) và đồng bộ hóa các luồng trong Java
Count Occurrences of a Char in a String
Java Program to Perform Postorder Recursive Traversal of a Given Binary Tree
Spring Boot - Sending Email
Java Program to Implement Hash Trie
Java Program to do a Breadth First Search/Traversal on a graph non-recursively
Hướng dẫn Java Design Pattern – Service Locator
Lớp TreeMap trong Java
Java Program to Implement Depth-limited Search
Java Program to implement Bit Matrix
Java Program to Check the Connectivity of Graph Using BFS
Java Program to Find MST (Minimum Spanning Tree) using Kruskal’s Algorithm
Java Program to Remove the Edges in a Given Cyclic Graph such that its Linear Extension can be Found
Từ khóa this và super trong Java
Spring Boot Actuator
Convert a Map to an Array, List or Set in Java
Java Multi-line String
Show Hibernate/JPA SQL Statements from Spring Boot
Java Program to Repeatedly Search the Same Text (such as Bible by building a Data Structure)
Quick Guide to Spring Bean Scopes
Giới thiệu Aspect Oriented Programming (AOP)
Java Program to Solve TSP Using Minimum Spanning Trees
Java Program to Check Cycle in a Graph using Graph traversal
Spring Boot - Admin Client