Java中集合删除元素时候关于ConcurrentModificationException的迷惑点
下面的示例來至于阿里巴巴Java開發(fā)手冊的集合處理部分的第7條:
運行如下代碼,會發(fā)現(xiàn)正確運行。
public static void hasNotExcption() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");for (String item : list1) {System.out.println("item : "+item);if ("1".equals(item)) {list1.remove(item);}}}但是運行如下代碼,則異常:java.util.ConcurrentModificationException(和1中的代碼區(qū)別是上面移除判斷條件是1,下面的判斷條件是2)
public static void hasExcption() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");for (String item : list1) {System.out.println("item : "+item);if ("2".equals(item)) { list1.remove(item);}}}?
再看如下代碼示例,運行結(jié)果見注釋:
/***沒有異常*/public static void hasNotExcption() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");list1.add("4");for (String item : list1) {System.out.println("item : " + item);// 移除1和3時候會拋ConcurrentModificationException異常// 但是移除2的時候不會拋出異常if ("3".equals(item)) {list1.remove(item);}}}/*** 有異常*/public static void hasExcption1() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");for (String item : list1) {// 增強的for循環(huán)底層實現(xiàn)使用的是迭代器if ("1".equals(item)) {// 移除并修改“modCount變量”,導(dǎo)致下次遍歷時候異常 list1.remove(item);}}}/*** 有異常*/public static void hasExcption2() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");for (String item : list1) {if ("3".equals(item)) {list1.remove(item);}}}通過上面三個例子是不是發(fā)現(xiàn)了結(jié)論?只要移除的是倒數(shù)第二個元素的話,就不會發(fā)生異常!的確是,移除倒數(shù)第二個元素的話就不會異常,那究竟是為什么呢?
?
知識點1:關(guān)于增強for循環(huán)的實現(xiàn)
集合的增強for循環(huán)實現(xiàn)內(nèi)部使用的是迭代器,可以通過eclipse的F5調(diào)試跟蹤。
List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");for (String item : list1) {if ("3".equals(item)) {list1.remove(item);}}?
知識點2:集合如果判定的是并發(fā)修改錯誤?
細(xì)心的話可以通過異常發(fā)現(xiàn)異常的判斷代碼是:
if (modCount != expectedModCount)throw new ConcurrentModificationException();其中的modCount是記錄集合的修改次數(shù),而expectedModCount是記錄的預(yù)期的修改次數(shù),當(dāng)集合的修改次數(shù)和預(yù)期修改次數(shù)不一致的時候則發(fā)生異常。
那么什么時候會修改modCount呢?當(dāng)我們對集合進(jìn)行刪除或者添加元素時候此時記錄集合的修改次數(shù)就會發(fā)生增加,但是期望的修改次數(shù)不會變化,當(dāng)檢查判斷異常條件時候就會根據(jù)條件處理。
?
到此回歸到上面問題,對于hasExcption1和hasExcption2是如何發(fā)生異常的?本例對hasExcption1進(jìn)行詳細(xì)解釋:
/*** 有異常*/public static void hasExcption1() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");// 增強的for循環(huán)底層實現(xiàn)使用的是迭代器。所以每一次都是調(diào)用一個hasNext,然后在調(diào)用next方法返回元素給itemfor (String item : list1) {if ("1".equals(item)) {// 移除并修改“modCount變量”,導(dǎo)致下次遍歷時候異常 list1.remove(item);}}}增強的for循環(huán)底層實現(xiàn)使用的是迭代器。所以每一次都是調(diào)用一個hasNext,然后在調(diào)用next方法返回元素給item。當(dāng)我們刪除元素之后,下一次循環(huán)時候會先調(diào)用hasNext是否還有元素,此時還有。在調(diào)用next方法返回該元素給item,但是調(diào)用next方法時候會檢測
集合修改次數(shù)和預(yù)期修改次數(shù)是否相等,如果不等的話則拋出異常。(本例是不等的,因為移除1之后modCount=4,而expectModCount=3)。
?
那么為什么刪除倒數(shù)第二個元素不會異常呢?示例代碼:
/***沒有異常*/public static void hasNotExcption() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");list1.add("4");for (String item : list1) {if ("3".equals(item)) {list1.remove(item);}}}因為我們刪除倒數(shù)第二個元素時候,此時size=size-1,當(dāng)刪除完元素進(jìn)入下一次循環(huán)時候,此時hasNext方法判斷是否還有元素的時候返回是false(hasNext使用的是游標(biāo)和size進(jìn)行比較)。所以就不會調(diào)用next方法了。上面例子我們知道異常就是在next方法中拋的,所以
刪除倒數(shù)第二個元素就不會有異常。
?
注意:如果刪除的是倒數(shù)第二個元素那么最后的結(jié)果是否正確呢?答案:顯然是不正確的。
通過如下示例代碼檢驗:
/***沒有異常*/public static void hasNotExcption() {List<String> list1 = new ArrayList<String>();list1.add("1");list1.add("2");list1.add("3");list1.add("3");for (String item : list1) {if ("3".equals(item)) {list1.remove(item);}}// 顯然最后一個3沒有刪除,所以結(jié)果錯誤System.out.println(list1); //[1, 2, 3] }結(jié)果是錯誤的,原因:因為最后一個元素由于在移除倒數(shù)第二個元素時候?qū)ize-1了,所以遍歷集合提前結(jié)束了。所以沒有完全移除掉所有的“3”
?
轉(zhuǎn)載于:https://www.cnblogs.com/leodaxin/p/7651630.html
總結(jié)
以上是生活随笔為你收集整理的Java中集合删除元素时候关于ConcurrentModificationException的迷惑点的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 洛谷3857 [TJOI2008]彩灯
- 下一篇: ObservableCollection