Java中List遍歷刪除元素remove()的方法
今天碰見根據(jù)條件進(jìn)行l(wèi)ist遍歷remove的問題,第一時(shí)間就是簡單for循環(huán)remove,只知道這么寫不行,不安全,可是為什么呢?你想過嗎?下面就關(guān)于List遍歷remove的問題,深挖一下!
一、幾種常見的遍歷方式
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ù)索引訪問第2個(gè)元素時(shí),因?yàn)閯h除的關(guān)系,后面的元素都往前移動(dòng)了一位,所以實(shí)際訪問的是第3個(gè)元素。所以這種方式可以用在刪除特定的一個(gè)元素時(shí)使用,但不適合循環(huán)刪除多個(gè)元素時(shí)使用。
2、高級(jí)for循環(huán)出錯(cuò)原因
foreach其實(shí)是用迭代器來進(jìn)行遍歷的,而在遍歷時(shí)直接使用arraylist的remove方法會(huì)導(dǎo)致什么問題呢?
可以再看一下fastremove和迭代器遍歷的內(nèi)部代碼:


最后導(dǎo)致拋出上面異常的其實(shí)就是這個(gè),簡單說,調(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)集合中元素過多時(shí),復(fù)制list就顯得有些笨重了,采用迭代器的方式進(jìn)行遍歷較快一些,并且不用關(guān)注小角標(biāo)的變化。
3、不考慮性能的時(shí)候使用removeIf方法,代碼簡潔明了。
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. idea設(shè)置自動(dòng)導(dǎo)入依賴的方法步驟2. XML在語音合成中的應(yīng)用3. Django中如何使用Channels功能4. docker容器調(diào)用yum報(bào)錯(cuò)的解決辦法5. IDEA下因Lombok插件產(chǎn)生的Library source does not match the bytecode報(bào)錯(cuò)問題及解決方法(親測(cè)可用)6. 使用 kind 和 Docker 啟動(dòng)本地的 Kubernetes環(huán)境7. IntelliJ IDEA導(dǎo)出項(xiàng)目的方法8. python的json包位置及用法總結(jié)9. python如何操作mysql10. 關(guān)于html嵌入xml數(shù)據(jù)島如何穿過樹形結(jié)構(gòu)關(guān)系的問題

網(wǎng)公網(wǎng)安備