今天看面試題的時候,看到這個問題的剖析,覺得挺不錯
寫一段代碼在遍歷 ArrayList 時移除一個元素?
該問題的關鍵在於面試者使用的是 ArrayList 的 remove() 還是 Iterator 的 remove()方法。是使用正確的方式來實現在遍歷的過程中移除元素,而不會出現 ConcurrentModificationException 異常的示例代碼。
於是對ArrayList移除一個元素的相關知識點進行了拓展,查閱到了這些資料:
今天寫了一道題,題目是這樣的:
一個ArrayList對象aList中存有若干個字符串元素,現欲遍歷該ArrayList對象,刪除其中所有值爲"abc"的字符串元素,請用代碼實現。
很簡單,直接上代碼:
public class Test1 {
public static void main(String[] args) {
ArrayList<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("ab");
aList.add("abc");
aList.add("abcr");
aList.add("abc");
aList.add("abcf");
aList.add("abc");
aList.add("abdc");
for(int i = 0;i < aList.size();i++){
if(aList.get(i).equals("abc")){
aList.remove(i);
}
}
System.out.println(aList);
}
}
輸出結果爲:[a, ab, abcr, abcf, abdc]
也可以使用迭代器來遍歷:
Iterator<String> iter = aList.iterator();
while(iter.hasNext()){
if(iter.next().equals("abc")){
iter.remove();
}
結果與上面相同。
後來改了數據,多加了一個“abc”:
ArrayList<String> aList = new ArrayList<String>();
aList.add("a");
aList.add("ab");
aList.add("abc");
aList.add("abc"); //多加的一行
aList.add("abcr");
aList.add("abc");
aList.add("abcf");
aList.add("abc");
aList.add("abdc");
然後再用for循環遍歷,結果變爲:
[a, ab, abc, abcr, abcf, abdc] 發現有一個“abc”沒有被移除掉。
然而使用迭代器,答案是對的,所有的“abc”都被移除掉了。
原因:檢查後發現。在for循環裏,當清除掉前一個“abc”後,索引會指向下一個“abc”,然而還做了i++操作,等於直接將這個“abc”跳了過去去執行後面的步驟,從而使它“逃過法網”。
而迭代器不會有這樣的問題是因爲hasNext()方法,原理是指針向後移動,每運行一次it.next(),指針向後移動一次,一個一個的遍歷。
總結:可以在for循環中做一點小處理,如下:
for(int i = 0;i < aList.size();i++){
if(aList.get(i).equals("abc")){
aList.remove(i);
i--;
}
}
每次清除掉“abc”之後執行i–操作,下一回循環再執行i++操作,就相當於抵消啦。
爲了避免此類問題的出現,儘量還是用迭代器比較好。
還有一種辦法就是:我們知道ArrayList的底層是用數組實現的,如果你刪除了其中一個元素,那麼後邊的元素都會向前移動。所以在遍歷時如果刪除元素,就要小心了。用數組下標進行遍歷,如果需要刪除元素,我們從後向前遍歷,這樣不論有沒有元素刪除,我們都不會遺漏未被遍歷的元素。
這是ArrayList遍歷的時候刪除某元素的兩種辦法,我們還是儘量用迭代器更加的好
下面將針對java.util.ArrayList在foreach循環遍歷時刪除元素的問題
也就是此面試題中所說的ConcurrentModificationException 異常的問題(可能foreach循環只是出現這個異常的一種原因)
查閱了相關的資料,現在分享給大家
ArrayList是java開發時非常常用的類,常碰到需要對ArrayList循環刪除元素的情況。這時候大家都不會使用foreach循環的方式來遍歷List,因爲它會拋java.util.ConcurrentModificationException異常。比如下面的代碼就會拋這個異常:
List list = new ArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for (String item : list) {
if (item.equals("3")) {
System.out.println(item);
list.remove(item);
}
}
System.out.println(list.size());
那是不是在foreach循環時刪除元素一定會拋這個異常呢?答案是否定的。
見這個代碼:
Listlist=newArrayList();
list.add("1");
list.add("2");
list.add("3");
list.add("4");
list.add("5");
for(Stringitem:list){
if(item.equals("4")){
System.out.println(item);
list.remove(item);
}
}
System.out.println(list.size());
這段代碼和上面的代碼只是把要刪除的元素的索引換成了4,這個代碼就不會拋異常。爲什麼呢?
接下來先就這個代碼做幾個實驗,把要刪除的元素的索引號依次從1到5都試一遍,發現,除了刪除4之外,刪除其他元素都會拋異常。接着把list的元素個數增加到7試試,這時候可以發現規律是,只有刪除倒數第二個元素的時候不會拋出異常,刪除其他元素都會拋出異常。
好吧,規律知道了,可以從代碼的角度來揭開謎底了。
首先java的foreach循環其實就是根據list對象創建一個Iterator迭代對象,用這個迭代對象來遍歷list,相當於list對象中元素的遍歷託管給了Iterator,你如果要對list進行增刪操作,都必須經過Iterator,否則Iterator遍歷時會亂,所以直接對list進行刪除時,Iterator會拋出ConcurrentModificationException異常
其實,每次foreach迭代的時候都有兩部操作:
iterator.hasNext() //判斷是否有下個元素
item = iterator.next() //下個元素是什麼,並賦值給上面例子中的item變量
hasNext()方法的代碼如下:
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
這時候你會發現這個異常是在next方法的checkForComodification中拋出的,拋出原因是modCount != expectedModCount
modCount是指這個list對象從new出來到現在被修改次數,當調用List的add或者remove方法的時候,這個modCount都會自動增減;
expectedModCount是指Iterator現在期望這個list被修改的次數是多少次。
iterator創建的時候modCount被賦值給了expectedModCount,但是調用list的add和remove方法的時候不會同時自動增減expectedModCount,這樣就導致兩個count不相等,從而拋出異常。
如果想讓其不拋出異常,一個辦法是讓iterator在調用hasNext()方法的時候返回false,這樣就不會進到next()方法裏了。這裏cursor是指當前遍歷時下一個元素的索引號。比如刪除倒數第二個元素的時候,cursor指向最後一個元素的,而此時刪掉了倒數第二個元素後,cursor和size()正好相等了,所以hasNext()返回false,遍歷結束,這樣就成功的刪除了倒數第二個元素了。
破除迷信,foreach循環遍歷的時候不能刪除元素不是絕對,倒數第二個元素是可以安全刪除的~~(當然以上的思路都是建立在list沒有被多線程共享的情況下)
資料轉載於:http://blog.csdn.net/zhuhai__yizhi/article/details/49992321
http://blog.csdn.net/u011665766/article/details/50697580
http://blog.csdn.net/hongchangfirst/article/details/49780389
作者:弗蘭隨風小歡
來源:CSDN
原文:https://blog.csdn.net/qq_32575047/article/details/78902254
版權聲明:本文爲博主原創文章,轉載請附上博文鏈接!