Tránh lỗi ConcurrentModificationException trong Java như thế nào?
Bài viết được sự cho phép của tác giả Nguyễn Hữu Khanh
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.
Ví dụ xảy ra lỗi ConcurrentModificationException
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)
Ứng tuyển các vị trí việc làm Java lương cao trên TopDev
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)
Tránh lỗi ConcurrentModificationException
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)
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(); } }
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++]
Sử dụng phương thức removeIf() trong Java 8
languages.removeIf(language -> language.equals("C#") || language.equals("Ruby"));
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.
- T Từ khóa final trong Java
- R RxJava – Thời đại lập trình Reactive programming ứng dụng Android đã tới
- J Java roadmap cho newbie – Từ số 0 đến chuyên nghiệp
- T Top các Framework Java Backend phổ biến cho anh em Developer
- T Tìm hiểu về lớp HashMap trong Java: Các hoạt động và cách dùng
- L Lập trình Android bằng Java: Hướng dẫn toàn diện
- J Java Super là gì? Sự khác biệt giữa Java Super và Java thông thường
- K Khai báo phương thức overloading trong Java
- J Java Sleep: Kỹ thuật ngủ luồng trong lập trình Java
- S Sử dụng subString trong xử lý văn bản trong Java