Java中List遍歷刪除元素remove()的方法
今天碰見(jiàn)根據(jù)條件進(jìn)行l(wèi)ist遍歷remove的問(wèn)題,第一時(shí)間就是簡(jiǎn)單for循環(huán)remove,只知道這么寫不行,不安全,可是為什么呢?你想過(guò)嗎?下面就關(guān)于List遍歷remove的問(wèn)題,深挖一下!
一、幾種常見(jiàn)的遍歷方式
1、普通for循環(huán)
2、高級(jí)for循環(huán)
3、iterator和removeIf
4、stream()
5、復(fù)制
6、普通for循環(huán) --> 倒序方式
二、源碼篇
1、普通for循環(huán)出錯(cuò)原因
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 remove method that skips bounds checking and does not * return the value removed. */private void fastRemove(int index) { modCount++; int numMoved = size - index - 1; if (numMoved > 0) //remove會(huì)導(dǎo)致之后的元素往前移動(dòng),而下標(biāo)不改變時(shí)就會(huì)出現(xiàn)bug System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; // clear to let GC do its work}
我們?cè)趧h除某個(gè)元素后,list的大小發(fā)生了變化,這時(shí)候你的的索引也會(huì)發(fā)生變化,這時(shí)就會(huì)導(dǎo)致你在遍歷的時(shí)候漏掉某些元素。比如當(dāng)你刪除第1個(gè)元素后,我們?nèi)绻€是繼續(xù)根據(jù)索引訪問(wèn)第2個(gè)元素時(shí),因?yàn)閯h除的關(guān)系,后面的元素都往前移動(dòng)了一位,所以實(shí)際訪問(wèn)的是第3個(gè)元素。所以這種方式可以用在刪除特定的一個(gè)元素時(shí)使用,但不適合循環(huán)刪除多個(gè)元素時(shí)使用。
2、高級(jí)for循環(huán)出錯(cuò)原因
foreach其實(shí)是用迭代器來(lái)進(jìn)行遍歷的,而在遍歷時(shí)直接使用arraylist的remove方法會(huì)導(dǎo)致什么問(wèn)題呢?
可以再看一下fastremove和迭代器遍歷的內(nèi)部代碼:
最后導(dǎo)致拋出上面異常的其實(shí)就是這個(gè),簡(jiǎn)單說(shuō),調(diào)用list.remove()方法導(dǎo)致modCount和expectedModCount的值不一致而報(bào)異常
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException();}//調(diào)用next時(shí)會(huì)調(diào)用checkForComodification方法檢查 這兩個(gè)字段//而fastRemove里面只對(duì)modCount 進(jìn)行了改變 導(dǎo)致拋出異常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];}
所以遍歷時(shí)remove并不適用于foreach。
3、java8中新方法removeIf
//內(nèi)部其實(shí)就是迭代器遍歷default boolean removeIf(Predicate<? super E> filter) { Objects.requireNonNull(filter); boolean removed = false; final Iterator<E> each = iterator(); while (each.hasNext()) { if (filter.test(each.next())) { each.remove(); removed = true; } } return removed;}
和迭代器差不多,內(nèi)部實(shí)現(xiàn)也是迭代器。
三、總結(jié)
1、在不考慮內(nèi)存大小會(huì)不會(huì)出現(xiàn)OOM的時(shí)候,采取復(fù)制一個(gè)新的list的方法速度更快,適用于集合中對(duì)象不算多的時(shí)候,畢竟只需要add操作。
2、當(dāng)集合中元素過(guò)多時(shí),復(fù)制list就顯得有些笨重了,采用迭代器的方式進(jìn)行遍歷較快一些,并且不用關(guān)注小角標(biāo)的變化。
3、不考慮性能的時(shí)候使用removeIf方法,代碼簡(jiǎn)潔明了。
4、當(dāng)要針對(duì)角標(biāo)進(jìn)行元素的remove時(shí),使用倒序遍歷的方式最為妥當(dāng)。
到此這篇關(guān)于Java中List遍歷刪除元素remove()的方法的文章就介紹到這了,更多相關(guān)Java List遍歷刪除元素內(nèi)容請(qǐng)搜索好吧啦網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持好吧啦網(wǎng)!
相關(guān)文章:
1. python爬蟲實(shí)戰(zhàn)之制作屬于自己的一個(gè)IP代理模塊2. HTML 絕對(duì)路徑與相對(duì)路徑概念詳細(xì)3. 基于javaweb+jsp實(shí)現(xiàn)企業(yè)財(cái)務(wù)記賬管理系統(tǒng)4. 如何在jsp界面中插入圖片5. 解決ajax請(qǐng)求后臺(tái),有時(shí)收不到返回值的問(wèn)題6. python實(shí)現(xiàn)PolynomialFeatures多項(xiàng)式的方法7. 使用FormData進(jìn)行Ajax請(qǐng)求上傳文件的實(shí)例代碼8. .NET6打包部署到Windows Service的全過(guò)程9. Ajax返回值類型與用法實(shí)例分析10. css代碼優(yōu)化的12個(gè)技巧
