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

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程语言 > java >内容正文

java

foreach去除重复元素java_Java foreach 中List移除元素抛出ConcurrentModificationException原因全解析...

發(fā)布時(shí)間:2025/4/16 java 25 豆豆
生活随笔 收集整理的這篇文章主要介紹了 foreach去除重复元素java_Java foreach 中List移除元素抛出ConcurrentModificationException原因全解析... 小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

本文重點(diǎn)探討 foreach 循環(huán)中List 移除元素造成?java.util.ConcurrentModificationException 異常的原因。

先看《阿里巴巴 Java開發(fā)手冊(cè)》中的相關(guān)規(guī)定:

那么思考幾個(gè)問題:反例的運(yùn)行結(jié)果怎樣?

造成這種現(xiàn)象的根本原因是什么?

有沒有更優(yōu)雅地的移除元素姿勢(shì)?

本文將為你深度解讀該問題。

2.0 反例源代碼

public?class?ListExceptionDemo?{

public?static?void?main(String[]?args)?{

List?list?=?new?ArrayList<>();

list.add("1");

list.add("2");

for?(String?item?:?list)?{

if?("1".equals(item))?{

list.remove(item);

}

}

}

}

2.1 反例的運(yùn)行結(jié)果

當(dāng) if 的判斷條件是 “1”.equals(item) 時(shí),程序沒有拋出任何異常。if?("1".equals(item))?{

list.remove(item);

}

而當(dāng)判斷條件是 :"2".equals(item)時(shí),運(yùn)行會(huì)報(bào) java.util.ConcurrentModificationException。

2.2 原因分析

2.2.1 錯(cuò)誤提示

既然報(bào)錯(cuò),那么好辦,直接看錯(cuò)誤提示唄。Exception in thread "main" java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)

at java.util.ArrayList$Itr.next(ArrayList.java:859)

at com.chujianyun.common.collection.list.ListExceptionDemo.main(ListExceptionDemo.java:13)

啥?ConcurrentModificationException? 并發(fā)修改異常? 一個(gè)線程哪來的并發(fā)呢?

對(duì)應(yīng)的時(shí)序圖

然后我們通過錯(cuò)誤提示看源碼:我們看到錯(cuò)誤的原因是執(zhí)行 ArrayList的 Itr.next 取下一個(gè)元素檢查 并發(fā)修改是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];

}

modCount 和 expectedModCount不一致導(dǎo)致的:final?void?checkForComodification()

{

if?(modCount?!=?expectedModCount)

throw?new?ConcurrentModificationException();

}

因此可以推測(cè)出發(fā)生異常的根本原因在于:取下一個(gè)元素時(shí),檢查 modCount,發(fā)現(xiàn)不一致。

2.2.2 代碼調(diào)試法

為了驗(yàn)證上面的推測(cè),大家可以在上述兩個(gè)關(guān)鍵函數(shù)上打斷點(diǎn),通過單步了解程序的運(yùn)行步驟。

我們通過調(diào)試可以“觀察到”,ArrayList中的?foreach 循環(huán)的語法糖最終迭代器Array$Itr 實(shí)現(xiàn)的。

通過斷點(diǎn)我們發(fā)現(xiàn),ArrayList 構(gòu)造內(nèi)部類 Itr 對(duì)象時(shí)?expectedModCount 的值為 ArrayList的 modCount。

運(yùn)行 next 函數(shù)時(shí)會(huì)檢查L(zhǎng)ist 中的 modCount 的值 和 構(gòu)造迭代器時(shí)“備份的” expectedModCount 是否相等。

通過調(diào)試我們還發(fā)現(xiàn):雖然原始 list 至于兩個(gè)元素,for each 循環(huán)執(zhí)行兩次后,滿足if 條件移除 值為“2”的元素之后, foreach 循環(huán)依然可以進(jìn)入,此時(shí)會(huì)再次通過 next 取出 list中的元素,又會(huì)執(zhí)行? checkForComodification函數(shù)檢查上述兩個(gè)值是否相等,此時(shí)不等,拋出異常。

那么這里有存在兩個(gè)問題:為什么 List 為 2? , next 卻執(zhí)行了 3 次呢?

如果不通過調(diào)試我們?cè)趺粗?foreach 語法糖的底層如何實(shí)現(xiàn)的呢?

帶著這兩個(gè)問題,我們繼續(xù)深入研究下去。

2.2.3? 源碼解析

我們查看? ArrayList$Itr 的 hasNext 函數(shù):private?class?Itr?implements?Iterator?{

int?cursor;???????//?index?of?next?element?to?return

int?lastRet?=?-1;?//?index?of?last?element?returned;?-1?if?no?such

int?expectedModCount?=?modCount;

Itr(){}

public?boolean?hasNext()?{

return?cursor?!=?size;

}

//?其他省略

}

發(fā)現(xiàn)ArrayList的迭代器判斷是否有下一個(gè)元素的標(biāo)準(zhǔn)是將下一個(gè)待返回的元素的索引和 size 比,不等表示還有下一個(gè)元素。

我們重新看源碼:public?static?void?main(String[]?args)?{

List?list?=?new?ArrayList<>();

list.add("1");

list.add("2");

for?(String?item?:?list)?{

if?("2".equals(item))?{

list.remove(item);

}

}

}

最初 List 中有兩個(gè)元素,expectedModCount ?值為2。

遍歷第一個(gè)時(shí)沒有走到if, 遍歷第二個(gè)元素時(shí)走到if ,通過 List.remove 函數(shù)移除了元素。public?boolean?remove(Object?o)?{

if?(o?==?null)?{

for?(int?index?=?0;?index?

if?(elementData[index]?==?null)?{

fastRemove(index);

return?true;

}

}?else?{

for?(int?index?=?0;?index?

if?(o.equals(elementData[index]))?{

fastRemove(index);

return?true;

}

}

return?false;

}

而remove會(huì)調(diào)用 fastRemove 函數(shù)實(shí)際移除掉元素,在此函數(shù)中會(huì)將 modCount+1,即 modCount的值為3。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

}

因此在次進(jìn)入foreach 時(shí),expectedModCount ?值 和?modCount的值 不相等,因此認(rèn)為還有下一個(gè)元素。

但是調(diào)用迭代器的 next 函數(shù)時(shí)需檢查兩者是相等,發(fā)現(xiàn)不等,拋出ConcurrentModificationException異常。

當(dāng) if條件是? “1”.equals(item)時(shí)public?static?void?main(String[]?args)?{

List?list?=?new?ArrayList<>();

list.add("1");

list.add("2");

for?(String?item?:?list)?{

if?("1".equals(item))?{

list.remove(item);

}

}

}

循環(huán)取出第一個(gè)元素后直接通過list給移除掉了,再次進(jìn)入 foreach循環(huán)時(shí),通過 hashNext 判斷是否有下一個(gè)元素時(shí),由于 游標(biāo)==1(此時(shí)list的 size),因此判斷沒下一個(gè)元素。

也就是說此時(shí)循環(huán)只執(zhí)行了一次就結(jié)束了,沒有走到可以拋出ConcurrentModificationException異常的任何函數(shù)中,從而沒有任何錯(cuò)誤。

讀到這里對(duì)迭代器的理解是不是又深了一層呢?

看到這里可能還有些同學(xué)對(duì) foreach 究竟底層怎么實(shí)現(xiàn)的仍然一知半解,那么請(qǐng)看下一部分。

2.2.4 反匯編

話不多說,直接反匯編:public?class?com.chujianyun.common.collection.list.ListExceptionDemo?{

public?com.chujianyun.common.collection.list.ListExceptionDemo();

Code:

0:?aload_0

1:?invokespecial?#1??????????????????//?Method?java/lang/Object."":()V

4:?return

public?static?void?main(java.lang.String[]);

Code:

0:?new???????????#2??????????????????//?class?java/util/ArrayList

3:?dup

4:?invokespecial?#3??????????????????//?Method?java/util/ArrayList."":()V

7:?astore_1

8:?aload_1

9:?ldc???????????#4??????????????????//?String?1

11:?invokeinterface?#5,??2????????????//?InterfaceMethod?java/util/List.add:(Ljava/lang/Object;)Z

16:?pop

17:?aload_1

18:?ldc???????????#6??????????????????//?String?2

20:?invokeinterface?#5,??2????????????//?InterfaceMethod?java/util/List.add:(Ljava/lang/Object;)Z

25:?pop

26:?aload_1

27:?invokeinterface?#7,??1????????????//?InterfaceMethod?java/util/List.iterator:()Ljava/util/Iterator;

32:?astore_2

33:?aload_2

34:?invokeinterface?#8,??1????????????//?InterfaceMethod?java/util/Iterator.hasNext:()Z

39:?ifeq??????????72

42:?aload_2

43:?invokeinterface?#9,??1????????????//?InterfaceMethod?java/util/Iterator.next:()Ljava/lang/Object;

48:?checkcast?????#10?????????????????//?class?java/lang/String

51:?astore_3

52:?ldc???????????#6??????????????????//?String?2

54:?aload_3

55:?invokevirtual?#11?????????????????//?Method?java/lang/String.equals:(Ljava/lang/Object;)Z

58:?ifeq??????????69

61:?aload_1

62:?aload_3

63:?invokeinterface?#12,??2???????????//?InterfaceMethod?java/util/List.remove:(Ljava/lang/Object;)Z

68:?pop

69:?goto??????????33

72:?return

}

代碼偏移從 0 到 25 行實(shí)現(xiàn)下面這部分功能:List?list?=?new?ArrayList<>();

list.add("1");

list.add("2");

從 26行開始我們發(fā)現(xiàn)底層使用迭代器實(shí)現(xiàn),我們腦補(bǔ)后翻譯回 Java代碼大致如下:public?static?void?main(String[]?args)?{

List?list?=?new?ArrayList<>();

list.add("1");

list.add("2");

Iterator?iterator?=?list.iterator();

while?(iterator.hasNext())?{

String?item?=?iterator.next();

if?("2".equals(item))?{

//iterator.remove();

list.remove(item);

}

}

}

大家運(yùn)行“翻譯”后的代碼發(fā)信啊和原始代碼的報(bào)錯(cuò)內(nèi)容完全一致:Exception in thread "main" java.util.ConcurrentModificationException

at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)

at java.util.ArrayList$Itr.next(ArrayList.java:859)

at com.chujianyun.common.collection.list.ListException.main(ListException.java:16)

2.2.5 繼續(xù)深挖

1、為啥通過 iterator.remove() 移除元素就沒事呢?

我們看 java.util.ArrayList.Itr#remove 的源碼:public?void?remove()?{

if?(lastRet?

throw?new?IllegalStateException();

checkForComodification();

try?{

ArrayList.this.remove(lastRet);

cursor?=?lastRet;

lastRet?=?-1;

expectedModCount?=?modCount;

}?catch?(IndexOutOfBoundsException?ex)?{

throw?new?ConcurrentModificationException();

}

}

從這里我們看到,通過迭代器移除元素后, expectedModCount 會(huì)重新賦值為 modCount。

因此使用iterator.remove() 移除元素不報(bào)錯(cuò)的原因就找到了。

2、有沒有比手冊(cè)給出的代碼更優(yōu)雅的寫法?

我們打開其函數(shù)列表,觀察List 和其父類有沒有便捷地移除元素方式:

“驚奇”地發(fā)現(xiàn),Collection 接口提供了 removeIf 函數(shù)可以滿足此需求。

還等啥呢,替換下,發(fā)現(xiàn)代碼如此簡(jiǎn)潔:public?static?void?main(String[]?args)?{

List?list?=?new?ArrayList<>();

list.add("1");

list.add("2");????????//?一行代碼實(shí)現(xiàn)

list.removeIf("2"::equals);

}

自此是不是文章就該結(jié)束了呢?

NO..

removeIf 為啥能夠?qū)崿F(xiàn)移除元素的功能呢?

我們猜測(cè),底層應(yīng)該是遍歷然后對(duì)比元素然后移除,可能也是迭代器方式,我們看源碼:

java.util.Collection#removeIfdefault?boolean?removeIf(Predicate?super?E>?filter)?{

Objects.requireNonNull(filter);

boolean?removed?=?false;

final?Iterator?each?=?iterator();

while?(each.hasNext())?{

if?(filter.test(each.next()))?{

each.remove();

removed?=?true;

}

}

return?removed;

}

我們發(fā)現(xiàn)和我們想的比較一致。

本小節(jié)對(duì)《阿里巴巴 Java開發(fā)手冊(cè)》中 foreach 循環(huán) List 移除元素導(dǎo)致并發(fā)修改異常的問題,進(jìn)行了全面深入地剖析。

希望可以幫助大家,徹底搞懂這個(gè)問題。

另外也提供了研究類似問題的一般思路,即代碼調(diào)試、讀源碼、反匯編等。

通過這個(gè)問題,希望大家遇到問題時(shí),能夠養(yǎng)成深挖的精神,通過問題帶動(dòng)知識(shí)的理解,知其所以然。

最后提醒大家,不要看書記結(jié)論,容易忘,記住不會(huì)用,要多思考原因,才能理解更深刻。

“盡信書不如無書”,不要認(rèn)為作者寫的都是對(duì)的,都是最好的,要有自己的思考。

想了解更多《手冊(cè)》詳解的更多內(nèi)容,想學(xué)習(xí)更多開發(fā)和避坑技巧等,請(qǐng)關(guān)注《阿里巴巴Java 開發(fā)手冊(cè)》詳解專欄。

總結(jié)

以上是生活随笔為你收集整理的foreach去除重复元素java_Java foreach 中List移除元素抛出ConcurrentModificationException原因全解析...的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。

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

主站蜘蛛池模板: 日韩福利视频一区 | av网站免费大全 | 91黄色小视频 | 日本欧美国产 | 一区二区日韩视频 | 日韩影院在线 | 中文字幕一区二区三 | 亚洲第一国产 | 亚洲精品成人 | 中国一区二区视频 | 免费看黄在线观看 | 国产在线a视频 | 黄色片在线免费观看视频 | 美女扒开尿口让男人捅爽 | av网站在线免费观看 | 搡国产老太xxx网站 高h喷汁呻吟3p | 中文字幕亚洲色图 | 久免费一级suv好看的国产 | 中文字幕一区二区人妻痴汉电车 | 亚洲男女啪啪 | 日韩在线观看视频一区 | 日本不卡免费在线 | 成人试看120秒体验区 | 91理论片| 亚洲不卡在线 | 蜜桃av鲁一鲁一鲁一鲁俄罗斯的 | 青青草97国产精品免费观看 | 欧美久久久久久又粗又大 | 欧美大片免费 | 亚洲色图图 | bt男人天堂| 麻豆极品| 亚洲第一综合网站 | 欧美草比视频 | 二区在线播放 | 久久亚洲国产成人精品性色 | 人人艹人人爱 | 一级片在线免费 | 一本大道伊人av久久综合 | 国产第20页| 成人首页 | 色天天天| 亚洲一区二区三区成人 | 免费操片 | 天天摸日日摸狠狠添 | 精品国产一区二区三区四区阿崩 | 高清视频一区二区三区 | 免费a v网站 | av手机在线| 欧美日韩在线播放 | 欧美色图五月天 | 国产亚洲精品美女久久久 | 秋霞三区| 五月婷婷亚洲综合 | 台湾性生生活1 | 日本熟妇毛耸耸xxxxxx | 爱乃なみ加勒比在线播放 | 日日躁夜夜躁狠狠久久av | 免费在线观看黄色网址 | 91中文字幕在线播放 | 国产精品18久久久久久无码 | 精品动漫一区二区三区的观看方式 | 亚洲熟女乱色一区二区三区久久久 | 玖玖zyz | 亚洲天堂美女 | 欧美大片在线观看 | av导航在线 | 国产做爰免费观看 | 日本午夜啪啪 | 91精品91久久久中77777 | 亚洲天堂第一区 | 黄色一级在线观看 | 人人妻人人爽人人澡人人精品 | 国产中文字幕在线观看 | 国产成人精品视频ⅴa片软件竹菊 | 亚洲综合网站 | 美女光屁股视频 | 高清免费毛片 | 9·1·黄·色·视·频 | 亚洲综合图片一区 | 亚洲av午夜精品一区二区三区 | 可以免费看av的网站 | 中文在线a√在线8 | 美日韩成人av | 精品日韩av | 欧美日本亚洲 | av黄色网址 | 亚洲av高清一区二区三区 | 一本到在线 | 久久激情五月 | 国产精品久久久久99 | 波多野结衣视频一区二区 | 国产伦精品一区三区精东 | 久草视频福利在线 | 一本大道综合伊人精品热热 | 一级毛片aa| 日本精品一区 | 97成人人妻一区二区三区 | 伊人精品视频在线观看 |