廢話少說,,直接上測試代碼:
public class ForeachTest {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("hello");
list.add("hello1");
list.add("hello2");
// for (int i = 0; i < list.size(); i++) {
for(String s:list){
list.remove("hello");
System.out.println(s);
}
}
}
結(jié)果如下:
然后換一種遍歷方式:
public class ForeachTest {
public static void main(String[] args){
List<String> list = new ArrayList<>();
list.add("hello");
list.add("hello1");
list.add("hello2");
for (int i = 0; i < list.size(); i++) {
// for(String s:list){
list.remove("hello");
System.out.println(list.get(i));
}
}
}
結(jié)果如下:
以上是兩種測試的對比現(xiàn)象(結(jié)果),那么既然有了結(jié)果接下來就要分析原因了,。
控制臺拋異常首先定位異常拋出的地方,,通過查看ArrayList源碼可以得知異常觸發(fā)點(diǎn)為:
可知,異常的原因是因為modCount != expectedModCount導(dǎo)致的,。
接下來就是分別查看這兩個參數(shù)的含義和可以修改他們的操作了,。
- modCount:是AbstractList的一個成員變量:
protected transient int modCount = 0;
但是ArrayList是繼承AbstractList的,這里modCount的修飾符是protected,,所以是可以被subclass繼承的,。
繼續(xù)查看源碼,對modCount的英文解釋為:
The number of times this list has been <i>structurally modified</i>.翻譯:記錄該list結(jié)構(gòu)被修改的次數(shù),。
查看源碼中該變量的修改情況,,發(fā)現(xiàn)就是在add/remove的時候,修改了modeCount的值,。
-
expectedModCount:是ArrayList的一個內(nèi)部類的成員變量
說明:這里我們知道增強(qiáng)for底層的實現(xiàn)還是通過Iterator實現(xiàn)的,,所以這里就不難理解為什么出現(xiàn)在這個地方了。
-
在Iterator遍歷開始就設(shè)置了兩個參數(shù)相等,,那么是什么情況導(dǎo)致了兩個參數(shù)不等呢,?
前面我們知道在對集合進(jìn)行add和remove的時候會修改modCount的值,而我們整個iterator都沒有修改expectedModCount的值,,所以在我們使用增強(qiáng)for遍歷的時候,,如果進(jìn)行add/remove的操作,就會導(dǎo)致兩個參數(shù)不等,,然后拋出異常,。
fail-fast機(jī)制:以上我們是從代碼層面分析出了,為什么拋出異常,,但是我們其實還不是很清楚為什么要這么設(shè)計,。其實這是Java的fail-fast 機(jī)制,即快速失敗機(jī)制,,是java集合(Collection)中的一種錯誤檢測機(jī)制。
詳細(xì)解釋如下:
上面我們基本了解了異常原因和原理,,接下來的問題就是我們該如何在業(yè)務(wù)中處理這種情況呢,?
- 第一種方式:我們使用一般的for循環(huán)即可;
- 第二種方式:我們使用Iterator自帶的remove方法進(jìn)行操作,,因為自帶的方法是同步了modCount和expecteModCount的,;
- 第三種方式:使用Java8中的filter;
- 第四種方式:新建一個集合來裝過濾后的數(shù)據(jù)(這種方式比較消耗內(nèi)存,,不推薦使用);
- 第N種方式:已經(jīng)知道原因和原理了,,其實處理方式就很靈活了,,可以自己想想其他方式了,哈哈?。,。?/li>
|