java中List移除元素的四種方式
四種方式:
- 方式一,使用 Iterator ,順序向下,如果找到元素,則使用 remove 方法進行移除。
- 方式二,倒序遍歷 List ,如果找到元素,則使用 remove 方法進行移除。
- 方式三,正序遍歷 List ,如果找到元素,則使用 remove 方法進行移除,然后進行索引 “自減”。
- 方式四,使用jdk1.8新增的Stream流操作
1.Iterator 迭代器
@Test public void fun9(){ List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); // 獲取迭代器 Iterator<String> it = list.iterator(); while(it.hasNext()){ String str = it.next(); if("關(guān)羽".equals(str)){ it.remove(); } } System.out.println(list); }
2.倒序遍歷
@Test public void fun10(){ List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); for (int i = list.size() - 1; i > 0; i--) { if("關(guān)羽".equals(list.get(i))){ list.remove(i); } } System.out.println(list); }
3.正序遍歷
@Test public void fun11(){ List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); for (int i = 0; i < list.size(); i++) { if("關(guān)羽".equals(list.get(i))){ list.remove(i); i--; } } System.out.println(list); }
4.Stream流操作(JDK 1.8 +)
@Test public void fun8(){ List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); // 篩選出不是“關(guān)羽” 的集合 list = list.stream().filter(e -> !"關(guān)羽".equals(e)).collect(Collectors.toList()); System.out.println("method4|list=" + list); }
問題:
1.為什么不能使用forEach
@Test public void fun5(){ List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); for (String str :list) { if ("張飛".equals(str)){ list.remove(str); } } System.out.println(list); }
原因:
foreach方式遍歷元素的時候,是生成iterator,然后使用iterator遍歷。在生成iterator的時候,會保存一個expectedModCount參數(shù),這個是生成iterator的時候List中修改元素的次數(shù)。如果你在遍歷過程中刪除元素,List中modCount就會變化,如果這個modCount和exceptedModCount不一致,就會拋出異常。這個是為了安全的考慮。如果使用iterator遍歷過程中,使用List修改了元素,可能會出現(xiàn)不正常的現(xiàn)象。如果使用iterator的remove方法則會正常,因為iterator的remove方法會在內(nèi)部調(diào)用List的remove方法,但是會修改excepedModCount的值,因此會正常運行。
2.為什么forEach 刪除倒數(shù)第二元素不會出現(xiàn)異常
@Test public void fun12() { List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); for (String str:list) { if("關(guān)羽".equals(str)){ list.remove(str); } System.out.println(str); } }
仔細(xì)觀察發(fā)現(xiàn)集合最后一個元素(“張飛”)并沒有被遍歷出來,因為當(dāng)我們移除倒數(shù)第二個元素(“關(guān)羽”)時 cursor(游標(biāo))為 4 ,list 中size 屬性的值會發(fā)生變化(5 - 1 = 4)變?yōu)?4,所以下面代碼返回 false ,也就不會繼續(xù)向下遍歷。這也是能夠正常執(zhí)行的原因,因為如果繼續(xù)遍歷就會出現(xiàn)問題1 中的情況,在進行checkForComodification() 時,因為 modCount 發(fā)生了變化,而expectedModCount 并沒有發(fā)生變化,所以會出現(xiàn) ConcurrentModificationException異常。
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(); } }
public boolean hasNext() { return cursor != size; }
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]; }
final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); }
3 普通正序 for 循環(huán)為什么要 i –
因為遍歷過程中進行remove 操作時,該位置后面的元素會擠到前面來,這時候會發(fā)生一種情況就是原來元素的位置會被他后面的元素取代,而該位置已經(jīng)遍歷過了,所以該元素不會背遍歷。 所以要進行 i-- 操作從該位置重新遍歷。
@Test public void fun11(){ List<String> list = new ArrayList<>(); list.add("趙云"); list.add("黃忠"); list.add("馬超"); list.add("關(guān)羽"); list.add("張飛"); for (int i = 0; i < list.size(); i++) { System.out.println(list.get(i)); if("關(guān)羽".equals(list.get(i))){ list.remove(i); } } System.out.println(list); }
就是下面的情況 “張飛” 不見了…
4 為什么倒序for 循環(huán)可以
當(dāng)我們倒序遍歷元素的時候,無論刪除元素之后的元素怎么移動,之前的元素對應(yīng)的索引(index)是不會發(fā)生變化的,所以在刪除元素的時候不會發(fā)生問題。
到此這篇關(guān)于java中List移除元素的四種方式的文章就介紹到這了,更多相關(guān)java List移除元素內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Spring Bean生命周期之BeanDefinition的合并過程詳解
這篇文章主要為大家詳細(xì)介紹了Spring Bean生命周期之BeanDefinition的合并過程,文中示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下,希望能夠給你帶來幫助2022-03-03Spring中的@Scheduled定時任務(wù)注解詳解
這篇文章主要介紹了Spring中的@Scheduled定時任務(wù)注解詳解,要使用@Scheduled注解,首先需要在啟動類添加@EnableScheduling,啟用Spring的計劃任務(wù)執(zhí)行功能,這樣可以在容器中的任何Spring管理的bean上檢測@Scheduled注解,執(zhí)行計劃任務(wù),需要的朋友可以參考下2023-09-09Spring Boot使用Spring的異步線程池的實現(xiàn)
這篇文章主要介紹了Spring Boot使用Spring的異步線程池的實現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2020-02-02SpringBoot中的PropertySource原理詳解
這篇文章主要介紹了SpringBoot中的PropertySource原理詳解,PropertySource?是一個非常重要的概念,它允許您在應(yīng)用程序中定義屬性,并將這些屬性注入到?Spring?環(huán)境中,需要的朋友可以參考下2023-07-07一文快速了解spring?boot中的@idempotent注解
idempotence注解是RESTful API設(shè)計中一個重要的概念,它可以保證操作的可靠性和一致性,下面這篇文章主要給大家介紹了關(guān)于spring?boot中@idempotent注解的相關(guān)資料,需要的朋友可以參考下2024-01-01