編程過程中常常需要使用到集合,而ArrayList是我們常常使用的,但是最近在一次刪除和增加中出現了一些問題,分享記錄下。
分下如下倆段代碼
<code>List<string> arrayList1 = new ArrayList<string>();
arrayList1.add("1");
arrayList1.add("2");
for (String s : arrayList1) {
if("1".equals(s)){
arrayList1.remove(s);
}}
List<string> arrayList2 = new ArrayList<string>();
arrayList2.add("2");arrayList2.add("1");
for (String s : arrayList2) {
if("1".equals(s)){
arrayList2.remove(s);
}
}/<string>/<string>/<string>/<string>/<code>
程序運行結果如下:
arrayList1的remove方法成功執行, arrayList2的remove方法運行拋出ConcurrentModificationException異常。
我們查看源代碼來分析異常原因 因為foreach的本質就是使用迭代器Iterator,所有的Collecation集合類都會實現Iterable接口。 找到ArrayList類的iterator()方法
<code>public Iteratoriterator() { /<code>
return new Itr();
}
迭代器的本質是先調用hasNext()方法判斷存不存在下一元素,然後再使用next()方法取下一元素
<code>public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object\\[\\] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData\\[lastRet = i\\];
}/<code>
上面arraylist1為什麼能remove成功呢?其實它只循環了一次,所以成功了。
因為它在remove元素1之後,它的size-1變成1,然後Itr內部的cursor變量由0變成1,此時1=1,循環結束,所以成功了。
arraylist2為什麼remove失敗呢?因為它在循環第二次的時候,也remove成功了,但是第三次判斷next的時候cursor的值為2導致不等於現在的size 1,所以執行了next方法,最重要的來了,之前remove的操作導致ArrayList的modCount值加1,然後Itr類中的expectedModCount保持不變,所以會拋出異常。
<code>final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}/<code>
同理可得,由於add操作也會導致modCount自增,所以不允許在foreach中刪除, 增加,修改ArrayList中的元素。
對此,推薦大家使用迭代器Iterator刪除元素。
<code>Iterator<string> ite = arrayList2.iterator();
while(ite.hasNext()) {
if("1".equals(ite.next())) {
ite.remove();
}
}/<string>/<code>
如果存在併發操作,還需要對Iterator進行加鎖操作。
閱讀更多 Java編碼碼農 的文章