日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

为什么简单的删除集合中的元素竟然报错了?

發布時間:2024/8/23 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 为什么简单的删除集合中的元素竟然报错了? 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

作者 | 七十一

來源 |?程序員巴士

前言

什么是快速失敗:fail-fast 機制是java集合(Collection)中的一種錯誤機制。它只能被用來檢測錯誤,因為JDK并不保證fail-fast機制一定會發生。當多個線程對同一個集合的內容進行操作時,就可能會產生fail-fast事件。

運行如下代碼,即可出現異常:

//?關于fail-fast的一些思考 public?class?FailFastTest?{public?static?void?main(String[]?args)?{//?構建ArrayListList<Integer>?list?=?new?ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);for?(int?i?:?list)?{list.remove(1);}} }

控制臺會輸出如下異常:

為什么要報這個錯?途中出錯的地方是ArrayList中的代碼,定位到該處代碼:

final?void?checkForComodification()?{if?(modCount?!=?expectedModCount)throw?new?ConcurrentModificationException(); }

modCount是這個集合修改的次數,這個屬性來自AbstractList,而我們的ArrayList是繼承了該抽象類的。

protected?transient?int?modCount?=?0;

expectedModCount又是啥呢?當我們進行遍歷時候debug一下發現進行forEach循環的時候其實走了下面這個方法iterator,而且遍歷這個底層還是走的hasNext方法

public?Iterator<E>?iterator()?{return?new?Itr();}

判斷是否有下一個元素

public?boolean?hasNext()?{return?cursor?!=?size;}

next()方法用于獲取元素

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];}

點進這個new Itr(),驚喜的發現原來這個expectedModCount是在這里被賦值的而且和modCount一樣

private?class?Itr?implements?Iterator<E>?{int?cursor;???????//?index?of?next?element?to?returnint?lastRet?=?-1;?//?index?of?last?element?returned;?-1?if?no?suchint expectedModCount = modCount;?//?注意:此處進行賦值............

接下來看下ArrayList的remove()方法,其對modCount進行了增加,這是導致報錯的原因

public?E?remove(int?index)?{rangeCheck(index);modCount++;?//?對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?workreturn?oldValue;}

上面的next()方法這有調用一個checkForComodification()方法,下面貼一下這方法的代碼

final?void?checkForComodification()?{if?(modCount?!=?expectedModCount)throw?new?ConcurrentModificationException();}

ArrayList里面remove()方法進行了modCount++操作,原來是我們對集合進行操作后改變了modCount導致上面代碼成立,從而拋出異常

但是當我們使用Itr類的remove,也就是如下代碼進行對元素改動時,不會拋出ConcurrentModificationException異常

public?void?remove()?{if?(lastRet?<?0)throw?new?IllegalStateException();checkForComodification();try?{ArrayList.this.remove(lastRet);cursor?=?lastRet;lastRet?=?-1;//?將ArrayList的modCount賦值給Itr類的expectedModCount?//這樣再次調用next方法時就不會出現這倆個值不一致?從而避免報錯expectedModCount?=?modCount;?}?catch?(IndexOutOfBoundsException?ex)?{throw?new?ConcurrentModificationException();}}

與ArrayList的remove()方法不同的是,該remove()方法調用ArrayList.this.remove(lastRet);后顯然modCount++了,但是馬上又讓expectedModCount = modCount就是這樣才不會拋出異常。

梳理整個流程:

1、for循環遍歷實質上調用的是Itr類的方法進行遍歷(Itr類實現了Iterator)

2、Itr類在構造的時候會將ArrayList的modCount(實際上modCount是AbstractList的屬性,但是ArrayList繼承了AbstractList)賦值給Itr類的expectedModCount

3、for循環中調用的remove()方法時ArrayList的,這個方法會對modCount進行++操作

4、remove方法調用后,繼續遍歷會調用Itr的next()方法,而這個next()方法中的checkForComodification()方法會對modCount和expectedModCount進行對比,由于remove方法已經操作過modCount因此這倆個值不會相等,故報錯。

如何改進?

1、可以使用Itr中的remove方法進行改進,改進代碼如下

public?static?void?main(String[]?args)?{//?構建ArrayListList<Integer>?list?=?new?ArrayList<>();list.add(1);list.add(2);list.add(3);list.add(4);Iterator<Integer>?iterator?=?list.iterator();while(iterator.hasNext())?{iterator.next();iterator.remove();}System.out.println(list.size());?//?0}

2、使用CopyOnWriterArrayList來代替Arraylist,它對ArrayList的操作時會先復制一份數據出來操作完了再將其更新回去替換掉舊的,所以CopyOnWrite容器只能保證數據的最終一致性,不能保證數據的實時一致性。這是采用了CopyOnWriterArrayList的fail-safe機制,當集合的結構被改變的時候,fail-safe機制會在復制原集合的一份數據出來,然后在復制的那份數據遍歷,fail-safe機制,在JUC包的集合都是有這種機制實現的。

雖然fail-safe不會拋出異常,但存在以下缺點

1、復制時需要額外的空間和時間上的開銷。

2、不能保證遍歷的是最新內容。

總結

對于fail-fast機制,我們要操作List集合時可以使用Iterator的remove()方法在遍歷過程中刪除元素,或者使用fail-safe機制的CopyOnWriterArrayList,當然使用的時候需要權衡下利弊,結合相關業務場景。

往期推薦

低代碼發展專訪系列之八:低代碼平臺能夠打破企業「應用孤島」現象嗎?

Medusa又一個開源的替代品

用了HTTPS,沒想到還是被監控了

快速搭建實驗環境:使用 Terraform 部署 Proxmox 虛擬機

點分享

點收藏

點點贊

點在看

總結

以上是生活随笔為你收集整理的为什么简单的删除集合中的元素竟然报错了?的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。