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

歡迎訪(fǎng)問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 编程资源 > 编程问答 >内容正文

编程问答

移除List中的元素,你的姿势对了吗?

發(fā)布時(shí)間:2025/3/16 编程问答 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 移除List中的元素,你的姿势对了吗? 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

之前遇到對(duì)List進(jìn)行遍歷刪除的時(shí)候,出現(xiàn)來(lái)一個(gè)ConcurrentModificationException 異常,可能好多人都知道list遍歷不能直接進(jìn)行刪除操作,但是你可能只是跟我一樣知道結(jié)果,但是不知道為什么不能刪除,或者說(shuō)這個(gè)報(bào)錯(cuò)是如何產(chǎn)生的,那么我們今天就來(lái)研究一下。

?

一、異常代碼

我們先看下這段代碼,你有沒(méi)有寫(xiě)過(guò)類(lèi)似的代碼

public?static?void?main(String[]?args)?{List<Integer>?list?=?new?ArrayList<>();System.out.println("開(kāi)始添加元素?size:"?+?list.size());for?(int?i?=?0;?i?<?100;?i++)?{list.add(i?+?1);}System.out.println("元素添加結(jié)束?size:"?+?list.size());Iterator<Integer>?iterator?=?list.iterator();while?(iterator.hasNext())?{Integer?next?=?iterator.next();if?(next?%?5?==?0)?{list.remove(next);}}System.out.println("執(zhí)行結(jié)束?size:"?+?list.size()); }

「毫無(wú)疑問(wèn),執(zhí)行這段代碼之后,必然報(bào)錯(cuò),我們看下報(bào)錯(cuò)信息。」

我們可以通過(guò)錯(cuò)誤信息可以看到,具體的錯(cuò)誤是在checkForComodification 這個(gè)方法產(chǎn)生的。

?

二、ArrayList源碼分析

首先我們看下ArrayList的iterator這個(gè)方法,通過(guò)源碼可以發(fā)現(xiàn),其實(shí)這個(gè)返回的是ArrayList內(nèi)部類(lèi)的一個(gè)實(shí)例對(duì)象。

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

我們看下Itr類(lèi)的全部實(shí)現(xiàn)。

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;Itr()?{}public?boolean?hasNext()?{return?cursor?!=?size;}@SuppressWarnings("unchecked")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];}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();}}@Override@SuppressWarnings("unchecked")public?void?forEachRemaining(Consumer<??super?E>?consumer)?{Objects.requireNonNull(consumer);final?int?size?=?ArrayList.this.size;int?i?=?cursor;if?(i?>=?size)?{return;}final?Object[]?elementData?=?ArrayList.this.elementData;if?(i?>=?elementData.length)?{throw?new?ConcurrentModificationException();}while?(i?!=?size?&&?modCount?==?expectedModCount)?{consumer.accept((E)?elementData[i++]);}//?update?once?at?end?of?iteration?to?reduce?heap?write?trafficcursor?=?i;lastRet?=?i?-?1;checkForComodification();}final?void?checkForComodification()?{if?(modCount?!=?expectedModCount)throw?new?ConcurrentModificationException();} }

「參數(shù)說(shuō)明:」

cursor : 下一次訪(fǎng)問(wèn)的索引;

lastRet :上一次訪(fǎng)問(wèn)的索引;

expectedModCount :對(duì)ArrayList修改次數(shù)的期望值,初始值為modCount;

modCount :它是AbstractList的一個(gè)成員變量,表示ArrayList的修改次數(shù),通過(guò)add和remove方法可以看出;

「幾個(gè)常用方法:」

hasNext():

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

如果下一個(gè)訪(fǎng)問(wèn)元素的下標(biāo)不等于size,那么就表示還有元素可以訪(fǎng)問(wèn),如果下一個(gè)訪(fǎng)問(wèn)的元素下標(biāo)等于size,那么表示后面已經(jīng)沒(méi)有可供訪(fǎng)問(wèn)的元素。因?yàn)樽詈笠粋€(gè)元素的下標(biāo)是size()-1,所以當(dāng)訪(fǎng)問(wèn)下標(biāo)等于size的時(shí)候必定沒(méi)有元素可供訪(fǎng)問(wèn)。

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

注意下,這里面有兩個(gè)非常重要的地方,cursor初始值是0,獲取到元素之后,cursor 加1,那么它就是下次索要訪(fǎng)問(wèn)的下標(biāo),最后一行,將i賦值給了lastRet這個(gè)其實(shí)就是上次訪(fǎng)問(wèn)的下標(biāo)。

此時(shí),cursor變?yōu)榱?,lastRet變?yōu)榱?。

最后我們看下ArrayList的remove()方法做了什么?

public?boolean?remove(Object?o)?{if?(o?==?null)?{for?(int?index?=?0;?index?<?size;?index++)if?(elementData[index]?==?null)?{fastRemove(index);return?true;}}?else?{for?(int?index?=?0;?index?<?size;?index++)if?(o.equals(elementData[index]))?{fastRemove(index);return?true;}}return?false; }private?void?fastRemove(int?index)?{modCount++;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 }

「重點(diǎn):」

我們先記住這里,modCount初始值是0,刪除一個(gè)元素之后,modCount自增1,接下來(lái)就是刪除元素,最后一行將引用置為null是為了方便垃圾回收器進(jìn)行回收。

?

三、問(wèn)題定位

到這里,其實(shí)一個(gè)完整的判斷、獲取、刪除已經(jīng)走完了,此時(shí)我們回憶下各個(gè)變量的值:

cursor : 1(獲取了一次元素,默認(rèn)值0自增了1);

lastRet :0(上一個(gè)訪(fǎng)問(wèn)元素的下標(biāo)值);

expectedModCount :0(初始默認(rèn)值);

modCount :1(進(jìn)行了一次remove操作,變成了1);

不知道你還記不記得,next()方法中有兩次檢查,如果已經(jīng)忘記的話(huà),建議你往上翻一翻,我們來(lái)看下這個(gè)判斷:

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

當(dāng)modCount不等于expectedModCount的時(shí)候拋出異常,那么現(xiàn)在我們可以通過(guò)上面各變量的值發(fā)現(xiàn),兩個(gè)變量的值到底是多少,并且知道它們是怎么演變過(guò)來(lái)的。那么現(xiàn)在我們是不是清楚了ConcurrentModificationException異常產(chǎn)生的愿意呢!

「就是因?yàn)?#xff0c;list.remove()導(dǎo)致modCount與expectedModCount的值不一致從而引發(fā)的問(wèn)題。」

?

四、解決問(wèn)題

我們現(xiàn)在知道引發(fā)這個(gè)問(wèn)題,是因?yàn)閮蓚€(gè)變量的值不一致所導(dǎo)致的,那么有沒(méi)有什么辦法可以解決這個(gè)問(wèn)題呢!答案肯定是有的,通過(guò)源碼可以發(fā)現(xiàn),Iterator里面也提供了remove方法。

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();} }

你看它做了什么,它將modCount的值賦值給了expectedModCount,那么在調(diào)用next()進(jìn)行檢查判斷的時(shí)候勢(shì)必不會(huì)出現(xiàn)問(wèn)題。

那么以后如果需要remove的話(huà),千萬(wàn)不要使用list.remove()了,而是使用iterator.remove(),這樣其實(shí)就不會(huì)出現(xiàn)異常了。

public?static?void?main(String[]?args)?{List<Integer>?list?=?new?ArrayList<>();System.out.println("開(kāi)始添加元素?size:"?+?list.size());for?(int?i?=?0;?i?<?100;?i++)?{list.add(i?+?1);}System.out.println("元素添加結(jié)束?size:"?+?list.size());Iterator<Integer>?iterator?=?list.iterator();while?(iterator.hasNext())?{Integer?next?=?iterator.next();if?(next?%?5?==?0)?{iterator.remove();}}System.out.println("執(zhí)行結(jié)束?size:"?+?list.size()); }

「建議:」

另外告訴大家,我們?cè)谶M(jìn)行測(cè)試的時(shí)候,如果找不到某個(gè)類(lèi)的實(shí)現(xiàn)類(lèi),因?yàn)橛袝r(shí)候一個(gè)類(lèi)有超級(jí)多的實(shí)現(xiàn)類(lèi),但是你不知道它到底調(diào)用的是哪個(gè),那么你就通過(guò)debug的方式進(jìn)行查找,是很便捷的方法。

?

五、總結(jié)

其實(shí)這個(gè)問(wèn)題很常見(jiàn),也是很簡(jiǎn)單,但是我們做技術(shù)的就是把握細(xì)節(jié),通過(guò)追溯它的具體實(shí)現(xiàn),發(fā)現(xiàn)它的問(wèn)題所在,這樣你不僅僅知道這樣有問(wèn)題,而且你還知道這個(gè)問(wèn)題具體是如何產(chǎn)生的,那么今后不論對(duì)于你平時(shí)的工作還是面試都是莫大的幫助。

本期分享就到這里,謝謝各位看到此處,

記得點(diǎn)個(gè)贊呦!

有道無(wú)術(shù),術(shù)可成;有術(shù)無(wú)道,止于術(shù)

歡迎大家關(guān)注Java之道公眾號(hào)

好文章,我在看??

總結(jié)

以上是生活随笔為你收集整理的移除List中的元素,你的姿势对了吗?的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。