帶了一個(gè) 3 年的開發(fā),不會(huì)循環(huán)刪除 List 中的元素,心態(tài)崩了......

最近和某個(gè)朋友聊天,說(shuō)他手下的一個(gè)開發(fā),工作 3 年多了,一個(gè)需求的技術(shù)點(diǎn),需要循環(huán)刪除 List 中的元素,整了半天,說(shuō)程序報(bào)錯(cuò),不會(huì)弄。。
他挺無(wú)語(yǔ)的,和我傾訴,我說(shuō)工作 3 年多也不至于吧,不會(huì)的話,在網(wǎng)上找找也能搞定啊,他說(shuō)確實(shí)是的,這個(gè)開發(fā)挺難帶的,簡(jiǎn)直崩潰??!
循環(huán)刪除 List 中的元素,這個(gè)問(wèn)題是有需要的注意點(diǎn)的,如果是個(gè)新手,確實(shí)會(huì)遇到一點(diǎn)麻煩,但工作 3 年多,我覺(jué)得應(yīng)該不至于啊,好吧,這篇文章就來(lái)梳理一下這其中的道道。
問(wèn)題
比如有以下這個(gè) List:
怎么刪除 List 中姓李的人?
方法
我來(lái)們分析下有可行的方法!
1、普通 for 循環(huán)刪除(不可靠)
輸出結(jié)果:
[張三, 周一, 劉四, 李白]
WC,李白沒(méi)刪干凈?
問(wèn)題就出在 list.size(),因?yàn)?list.size() 和 i 都是動(dòng)態(tài)變化的,i 的值一直在累加,list.size() 一直在減少,所以 list 就會(huì)早早結(jié)束了循環(huán)。
所以這種方式雖然不會(huì)報(bào)錯(cuò),但存在隱患,并且不容易被察覺(jué),不建議使用。
2、普通 for 循環(huán)提取變量刪除(拋異常)
把上面的示例中的 size 提出變量:
輸出結(jié)果:

好家伙,都直接干下標(biāo)溢出異常了。。
這里也很明顯,因?yàn)?size 變量是固定的,但 list 的實(shí)際大小是不斷減小的,而 i 的大小是不斷累加的,一旦 i >= list 的實(shí)際大小肯定就異常了。
3、普通 for 循環(huán)倒序刪除(可靠)
輸出結(jié)果:
[張三, 周一, 劉四]
結(jié)果輸出正常,這種刪除方式就算把 list.size() 提出變量也是 OK 的,因?yàn)檠h(huán)中只用到了一次。
4、增強(qiáng) for 循環(huán)刪除(拋異常)
輸出結(jié)果:

好家伙,又拋異常了。不過(guò)這次的異常和上面的下標(biāo)異常不太一樣,這次是:
java.util.ConcurrentModificationException
這個(gè)是集合操作中很常見(jiàn)的異常之一,即并發(fā)修改異常!
其實(shí),for(xx in xx) 就是增強(qiáng)的 for循環(huán),即迭代器 Iterator 的加強(qiáng)實(shí)現(xiàn),其內(nèi)部是調(diào)用的 Iterator 的方法,為什么會(huì)報(bào) ConcurrentModificationException 錯(cuò)誤,我們來(lái)看下源碼:



取下個(gè)元素的時(shí)候都會(huì)去判斷要修改的數(shù)量(modCount)和期待修改的數(shù)量(expectedModCount)是否一致,不一致則會(huì)報(bào)錯(cuò),而 ArrayList 中的 remove 方法并沒(méi)有同步期待修改的數(shù)量(expectedModCount)值,所以會(huì)拋異常了。
5、迭代器循環(huán)迭代器刪除(可靠)
輸出結(jié)果:
[張三, 周一, 劉四]
結(jié)果輸出正常,這是因?yàn)榈髦械?remove 方法將期待修改的數(shù)量(expectedModCount)值進(jìn)行了同步:

6、迭代器循環(huán)集合刪除(拋異常)
輸出結(jié)果:

又是那個(gè)并發(fā)修改異常,這個(gè)示例雖然使用了 Iterator 循環(huán),但刪除的時(shí)候卻使用了 list.remove 方法,同樣是有問(wèn)題的,注意了,千萬(wàn)別用錯(cuò)了。
7、集合 forEach 方法循環(huán)刪除(拋異常)
輸出結(jié)果:

forEach 源碼:

forEach 方法的背后其實(shí)就是增強(qiáng)的 for 循環(huán),底層即迭代器,所以使用 list.remove 同樣拋出?ConcurrentModificationException?異常。
8、stream filter 過(guò)濾(可靠)
輸出結(jié)果:
[張三, 周一, 劉四]
這個(gè)方法即是利用了 Stream 的篩選功能,快速過(guò)濾所需要的元素,雖然不是進(jìn)行集合刪除,但達(dá)到了同樣的目的,這種方法要更簡(jiǎn)潔吧。
總結(jié)
本文總結(jié)了 8 種循環(huán)刪除 List 元素的方法:
普通 for 循環(huán)刪除(不可靠)
普通 for 循環(huán)提取變量刪除(拋異常)
普通 for 循環(huán)倒序刪除(可靠)
增強(qiáng) for 循環(huán)刪除(拋異常)
迭代器循環(huán)迭代器刪除(可靠)
迭代器循環(huán)集合刪除(拋異常)
集合 forEach 方法循環(huán)刪除(拋異常)
stream filter 過(guò)濾(可靠)
可靠、可行的方案也只有 3 種,正統(tǒng)的刪除法也就 2 種,但也要注意別用錯(cuò)了方法,所以也理解那個(gè)小伙子說(shuō)怎么搞都搞不定,估計(jì)也是走了不少?gòu)澛?,或者沒(méi)有找對(duì)方法吧。