為什么在foreach循環(huán)中JAVA集合不能添加或刪除元素
1. 編碼強(qiáng)制規(guī)約
在《阿里巴巴Java開(kāi)發(fā)手冊(cè)》中,針對(duì)集合操作,有一項(xiàng)規(guī)定,如下:
【強(qiáng)制】不要在 foreach 循環(huán)里進(jìn)行元素的 remove/add 操作。remove 元素請(qǐng)使用 Iterator方式,如果并發(fā)操作,需要對(duì) Iterator 對(duì)象加鎖。
public class SimpleTest {
public static void main(String[] args) {
List<String> list = Lists.newArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
//正例
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String item = iterator.next();
if ("1".equalsIgnoreCase(item)) {
iterator.remove();
}
}
//反例
for (String item : list) {
if ("2".equals(item)) {
list.remove(item);
}
}
}
}
2. 原因分析
在循環(huán)或迭代時(shí),會(huì)首先創(chuàng)建一個(gè)迭代實(shí)例,這個(gè)迭代實(shí)例的expectedModCount 賦值為集合的modCount.
每當(dāng)?shù)魇?#12132; hashNext() / next() 遍歷下⼀個(gè)元素之前,都會(huì)檢測(cè) modCount 變量與expectedModCount 值是否相等,相等的話就返回遍歷;否則就拋出異?!綜oncurrentModificationException】,終⽌遍歷
如果在循環(huán)中添加或刪除元素,是直接調(diào)用集合的add,remove方法【導(dǎo)致了modCount增加或減少】,但這些方法不會(huì)修改迭代實(shí)例中的expectedModCount,導(dǎo)致在迭代實(shí)例中expectedModCount 與 modCount的值不相等,拋出ConcurrentModificationException異常
但迭代器中的remove,add方法,會(huì)在調(diào)用集合的remove,add方法后,將expectedModCount 重新賦值為modCount,所以在迭代器中增加、刪除元素是可以正常運(yùn)行的。
可以參考ArrayList中的內(nèi)部私有類Itr、ListItr的源碼
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
//刪除了一些代碼
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
3. 相關(guān)知識(shí)介紹
3.1. 什么是快速失敗(fail-fast)?
快速失敗(fail-fast) 是 Java 集合的⼀種錯(cuò)誤檢測(cè)機(jī)制。在使⽤迭代器對(duì)集合進(jìn)⾏遍歷的時(shí)候,在多線程下操作⾮安全失敗(fail-safe)的集合類可能就會(huì)觸發(fā) fail-fast 機(jī)制,導(dǎo)致拋出ConcurrentModificationException 異常。
另外,在單線程下,如果在遍歷過(guò)程中對(duì)集合對(duì)象的內(nèi)容進(jìn)⾏了修改的話也會(huì)觸發(fā) fail-fast 機(jī)制。
舉個(gè)例⼦:多線程下,如果線程 1 正在對(duì)集合進(jìn)⾏遍歷,此時(shí)線程 2 對(duì)集合進(jìn)⾏修改(增加、刪除、修改),或者線程 1 在遍歷過(guò)程中對(duì)集合進(jìn)⾏修改,都會(huì)導(dǎo)致線程 1 拋出ConcurrentModificationException 異常。
3.2. 什么是安全失敗(fail-safe)呢?
采⽤安全失敗機(jī)制的集合容器,在遍歷時(shí)不是直接在集合內(nèi)容上訪問(wèn)的,⽽是先復(fù)制原有集合內(nèi)容,在拷⻉的集合上進(jìn)⾏遍歷。所以,在遍歷過(guò)程中對(duì)原集合所作的修改并不能被迭代器檢測(cè)到,故不會(huì)拋ConcurrentModificationException 異常。
到此這篇關(guān)于為什么在foreach循環(huán)中JAVA集合不能添加或刪除元素的文章就介紹到這了,更多相關(guān)JAVA集合添加或刪除元素內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
SpringBoot feign動(dòng)態(tài)設(shè)置數(shù)據(jù)源(https請(qǐng)求)
這篇文章主要介紹了SpringBoot如何在運(yùn)行時(shí)feign動(dòng)態(tài)添加數(shù)據(jù)源,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2021-08-08
java中Collections.sort排序函數(shù)用法詳解
本篇文章主要介紹了java中Collections.sort排序函數(shù)用法詳解,非常具有實(shí)用價(jià)值,需要的朋友可以參考下。2016-12-12
Activiti工作流學(xué)習(xí)筆記之自動(dòng)生成28張數(shù)據(jù)庫(kù)表的底層原理解析
這篇文章主要介紹了Activiti工作流學(xué)習(xí)筆記之自動(dòng)生成28張數(shù)據(jù)庫(kù)表的底層原理解析,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03
SpringSecurity整合jwt權(quán)限認(rèn)證的全流程講解
這篇文章主要介紹了SpringSecurity整合jwt權(quán)限認(rèn)證的全流程講解,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-06-06
Spring AOP實(shí)現(xiàn)復(fù)雜的日志記錄操作(自定義注解)
Spring AOP實(shí)現(xiàn)復(fù)雜的日志記錄操作(自定義注解),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
springMVC+velocity實(shí)現(xiàn)仿Datatables局部刷新分頁(yè)方法
下面小編就為大家分享一篇springMVC+velocity實(shí)現(xiàn)仿Datatables局部刷新分頁(yè)方法,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過(guò)來(lái)看看吧2018-02-02
Springboot基于Redisson實(shí)現(xiàn)Redis分布式可重入鎖源碼解析
這篇文章主要介紹了Springboot基于Redisson實(shí)現(xiàn)Redis分布式可重入鎖,本文通過(guò)案例源碼分析給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2022-03-03
詳解Spring Data JPA使用@Query注解(Using @Query)
本篇文章主要介紹了詳解Spring Data JPA使用@Query注解(Using @Query),具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07

