如何避免ConcurrentModificationException
ConcurrentModificationException出現原因:
1.在Java集合類中,當我們在使用iterator遍歷集合物件時,當集合被改變且在使用迭代器遍歷集合的時候,迭代器的next()方法將丟擲ConcurrentModificationException異常。
2.如果subList()生成一個新的List物件,那麼當修改原始List物件的結構時,則將丟擲ConcurrentModificationException
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
//由於集合被改變,所以接下來的it.next()會丟擲異常
myList.remove(value);
}
}
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("2")) {
//下面一條語句,由於我們正在更新myMap中的現有鍵值,因此其大小沒有更改,所以沒有獲取ConcurrentModificationException
//通過檢視原始碼,HashMap的next方法是呼叫nextNode().key;獲得的,而nextNode()判斷修改的標誌的是modCount != expectedModCount,即數量不變就為意味著沒有修改
//modCount 的定義:The number of times this HashMap has been structurally modified
myMap.put("1", "4");
//如果放開註釋,接下來的 it1.next()就會丟擲異常
// myMap.put("4", "4");
}
}
}
}
結果:
List Value:1
List Value:2
List Value:3
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList I t r . c h e c k F o r C o m o d i f i c a t i o n ( A r r a y L i s t . j a v a : 901 ) a t j a v a . u t i l . A r r a y L i s t Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList Itr.checkForComodification(ArrayList.java:901)atjava.util.ArrayListItr.next(ArrayList.java:851)
at ConcurrentModificationExceptionExample.main(ConcurrentModificationExceptionExample.java:20)
如何避免這個異常呢?
1.在單執行緒下,所以iterator的remove()方法從原集合中刪除一個元素
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
public class ConcurrentModificationExceptionExample {
public static void main(String args[]) {
List<String> myList = new ArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
it.remove();
}
}
System.out.println("List Value:" + myList);
Map<String, String> myMap = new HashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("2")) {
myMap.put("1", "4");
// myMap.put("4", "4");
it1.remove();
}
}
System.out.println("myMap:" + myMap);
}
}
執行結果:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Value:[1, 2, 4, 5]
Map Value:1
Map Value:2
Map Value:3
myMap:{1=4, 3=3}
在多執行緒環境中避免ConcurrentModificationException
1.您可以將列表轉換為陣列,然後在陣列上進行迭代。這種方法適用於中小型列表,但是如果列表很大,則對效能的影響很大。
2.如果使用的是JDK1.5或更高版本,則可以使用ConcurrentHashMap和CopyOnWriteArrayList類。建議使用此方法來避免併發修改異常。
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
public class AvoidConcurrentModificationException {
public static void main(String[] args) {
List<String> myList = new CopyOnWriteArrayList<String>();
myList.add("1");
myList.add("2");
myList.add("3");
myList.add("4");
myList.add("5");
Iterator<String> it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
System.out.println("List Value:" + value);
if (value.equals("3")) {
myList.remove("4");
myList.add("6");
myList.add("7");
myList.remove("3");
}
}
System.out.println("List Size:" + myList);
Map<String, String> myMap = new ConcurrentHashMap<String, String>();
myMap.put("1", "1");
myMap.put("2", "2");
myMap.put("3", "3");
Iterator<String> it1 = myMap.keySet().iterator();
while (it1.hasNext()) {
String key = it1.next();
System.out.println("Map Value:" + myMap.get(key));
if (key.equals("1")) {
myMap.remove("3");
myMap.put("4", "4");
myMap.put("5", "5");
}
}
System.out.println("Map Size:" + myMap);
}
}
結果:
List Value:1
List Value:2
List Value:3
List Value:4
List Value:5
List Size:[1, 2, 5, 6, 7]
Map Value:1
Map Value:2
Map Value:4
Map Value:5
Map Size:{1=1, 2=2, 4=4, 5=5}
為什麼CopyOnWriteArrayList使用iterator時修改原資料就不會報錯呢?
public Iterator<E> iterator() {
return new COWIterator<E>(getArray(), 0);
}
CopyOnWriteArrayList內部靜態類
static final class COWIterator<E> implements ListIterator<E> {
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
//傳入的是物件,所以原資料修改並不影響它
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}
public boolean hasNext() {
return cursor < snapshot.length;
}
public boolean hasPrevious() {
return cursor > 0;
}
..........
如果subList()生成一個新的List物件,那麼當修改原始List物件的結構時,則將丟擲ConcurrentModificationException
import java.util.ArrayList;
import java.util.List;
public class ConcurrentModificationExceptionWithArrayListSubList {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("Java");
names.add("PHP");
names.add("SQL");
names.add("Angular 2");
List<String> first2Names = names.subList(0, 2);
System.out.println(names + " , " + first2Names);
// names.set(1, "JavaScript");
// names.add("Oracle");
// check the output below. :)
System.out.println(names);
System.out.println(first2Names);
// Let's modify the list size and get ConcurrentModificationException
// names.add("NodeJS");
System.out.println(names); // this line throws exception
first2Names.set(1, "RabbitMQ");
// System.out.println(first2Names);
System.out.println(names);
System.out.println(first2Names);
names.add(1, "RabbitMQ2");
//下一行出現異常
first2Names.set(1, "RabbitMQ3");
}
}
結果:
[Java, PHP, SQL, Angular 2] , [Java, PHP]
[Java, PHP, SQL, Angular 2]
[Java, PHP]
[Java, PHP, SQL, Angular 2]
[Java, RabbitMQ, SQL, Angular 2]
[Java, RabbitMQ]
Exception in thread “main” java.util.ConcurrentModificationException
at java.util.ArrayList
S
u
b
L
i
s
t
.
c
h
e
c
k
F
o
r
C
o
m
o
d
i
f
i
c
a
t
i
o
n
(
A
r
r
a
y
L
i
s
t
.
j
a
v
a
:
1231
)
a
t
j
a
v
a
.
u
t
i
l
.
A
r
r
a
y
L
i
s
t
SubList.checkForComodification(ArrayList.java:1231) at java.util.ArrayList
SubList.checkForComodification(ArrayList.java:1231)atjava.util.ArrayListSubList.set(ArrayList.java:1027)
at ConcurrentModificationExceptionWithArrayListSubList.main(ConcurrentModificationExceptionWithArrayListSubList.java:34)
參考文章
CopyOnWriteArrayList
相關文章
- 如何避免VoIP安全風險
- 迭代器的 ConcurrentModificationExceptionException
- java.util.ConcurrentModificationExceptionJavaException
- java如何避免程式死鎖Java
- MySQL如何避免使用swap(ZT)MySql
- Flutter中如何避免多次buildFlutterUI
- 如何避免資料庫被黑資料庫
- [譯]如何避免golang的坑Golang
- GOLANG如何避免字串轉義Golang字串
- 如何避免應用安全風險?
- 如何避免空指標出錯?指標
- 美國伺服器如何避免踩雷?伺服器
- 創業路上,如何避免落坑?創業
- 如何避免javascript中的衝突JavaScript
- 程式設計師如何避免故障?程式設計師
- Java·ConcurrentModificationException的具體原因JavaException
- ConcurrentModificationException併發修改異常Exception
- iOS開發如何避免安全隱患iOS
- 如何避免前人挖坑,後人填坑
- 雲端計算如何避免隱性成本
- 如何避免在Flask中使用Response物件Flask物件
- 如何避免陷入教程地獄? - josh
- Java中的常量:如何避免反模式Java模式
- spring事務單態,如何避免???Spring
- 如何避免任務重複執行
- HashMap中ConcurrentModificationException異常解讀HashMapException
- 理解分析java集合操作之ConcurrentModificationExceptionJavaException
- 集合遍歷 java.util.ConcurrentModificationExceptionJavaException
- 網站如何避免谷歌SEO的懲罰?網站谷歌
- Flutter | 效能優化——如何避免應用 jankFlutter優化
- 分享如何避免您的網站被入侵?網站
- 中國傳統白酒:如何避免品牌熵增?熵
- CRM系統實施中如何避免“踩雷”
- 伺服器如何避免【資料洩露】?伺服器
- 如何識別和避免間諜軟體
- Java中如何避免空指標異常Java指標
- 教你如何避免威脅建模7大“坑”
- 在工作上,如何避免壞了友情?