List 集合遍歷過程中刪除元素避坑指南。

文章目錄

  • 1. 遍歷
  • 2. 遍歷過程中刪除元素
    • 2.1 for 簡單循環正向遍歷方式
    • 2.2 for 簡單循環反向遍歷方式
    • 2.3 foreach 方式遍歷刪除
    • 2.4 Iterator的remove()方法
    • 2.5 <font color = green> removeIf() (推薦)<green>
    • 2.6 Strem 方式


在這里插入圖片描述

作為一名后端開發,不管采用什么語言 使用 List 集合的頻率都非常高。

對 List 集合的遍歷和遍歷中操作數據也是家常便飯。

我從我使用的過程中對于此問題的思考與實踐形成記錄,與大家交流。有不對的地方,懇請大家指正。

1. 遍歷

List 集合的遍歷有很多種方式,每一種遍歷方式只有性能上的差異,不會有異常和暗坑。這里不再贅述。


2. 遍歷過程中刪除元素

因為 List 集合有多種遍歷方式,也就意味著存在多種遍歷中刪除的方式:

在做各種方式比對前,我們先加入一些初始數據。

 List<String> list = new ArrayList<>();list.add("snow");list.add("nier");list.add("sar");list.add("juaya");list.add("rock");list.add("snow");list.add("nier");

2.1 for 簡單循環正向遍歷方式

for (int i = 0; i < list.size(); i++) {String name = list.get(i);if(name.equals("snow")){list.remove(i);}if(name.equals("nier")){list.remove(i);}
}System.out.println(list);

上述代碼打印結果:

 [nier, sar, juaya, rock, nier]

很明顯 這不是正確的結果。
這是 因為:在遍歷過程中,刪除或者增加元素后,集合長度會因為實時改動而改動,也就是說集合在遍歷過程中忘了初心了。

舉個栗子:現在對長度為 5 的集合進行遍歷,在遍歷到 第三個元素的時候刪除了ta,那么此時集合長度成了4,那么此時集合就不會遍歷到之前下標為 5 的元素了。

知道了原因后,我們可以對癥下藥,達到我們的預期。修改代碼如下:

for (int i = 0; i < list.size(); i++) {String name = list.get(i);if(name.equals("snow")){list.remove(i);//	加補償機制i--;}if(name.equals("nier")){list.remove(i);//	加補償機制i--;}
}System.out.println(list);

上述代碼執行結果:

[sar, juaya, rock]

如此 加入了補償機制后雖然達到了預期結果。但是很明顯代碼不夠優雅。So 這種方式不推薦。


2.2 for 簡單循環反向遍歷方式

反向遍歷和正向遍歷類似,只不過是反方向的鐘而已。

for (int i = list.size() - 1; i >= 0; i--) {String name = list.get(i);if(name.equals("snow")){list.remove(i);}if(name.equals("nier")){list.remove(i);}
}System.out.println(list);

上述代碼執行結果:

[sar, juaya, rock]

咦~可以哎。結果正確。那這種方式是不是就絕對可以了呢。 NO!

逆序遍歷雖然在這個場景下達到了預期的正確結果,但是因為 這種方式和正序在原理上是相同的。所以在遍歷過程中操作元素也會有其劣根性。比如在逆序遍歷過程中加入元素,依然會對整個遍歷產生影響。 所以這也不是推薦的方式。


2.3 foreach 方式遍歷刪除

單從遍歷來看 這種方式比上述兩種方式優雅了不少。但是遍歷過程中操作元素能達到預期結果嗎?

for (String s : list) {if(s.equals("rock")){list.remove("rock");}
}

執行結果…
執行結果

What???

在這里插入圖片描述

原因: 這種循環方式,其生成的字節碼其實使用的 Iterator ,使用的核心方法是 `hasnext()` 和 `next()`。 然后再來看下ArrayList類的 Iterator 是如何實現的呢? Iterator 部分源碼:

Iterator 部分源碼

可以看出:調用next()方法獲取下一個元素時,第一行代碼就是調用了checkForComodification();

而該方法的核心邏輯就是比較 modCountexpectedModCount 這2個變量的值。
在上面的例子中,剛開始 modCountexpectedModCount 的值都為 n,所以第1次獲取元素 “rock” 是沒問題的,但是當執行完下面這行代碼時:

list.remove("rock");

modCount 的值就被修改了。
remove()
So: 使用 foreach 遍歷過程中刪除元素是不可行的。


2.4 Iterator的remove()方法

Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){String next = iterator.next();if("snow".equals(next)){iterator.remove();}
}

執行結果:

[nier, sar, juaya, rock, nier]

很 nice !

那這種方式怎么就可以刪除了呢???

因為:

這種方式每次刪除一個元素,都會將modCount 的值重新賦值給 expectedModCount,這樣2個變量就相等了,不會觸發 java.util.ConcurrentModificationException 異常。

在這里插入圖片描述

雖然這種方式符合了業務要求,但是還是不推薦這種方式,因為他的寫法依然不夠優雅。


2.5 removeIf() (推薦)

從JDK1.8開始,可以使用 removeIf() 方法來代替 Iterator的remove()方法實現一邊遍歷一邊刪除。

list.removeIf(s -> s.equals("snow"));System.out.println(list);

打印結果:

[nier, sar, juaya, rock, nier]

其源碼如下:
源碼


2.6 Strem 方式

list = list.stream().filter( name -> !("snow".equals(name) ) ).collect(Collectors.toList());System.out.println(list);

filter 過濾會保留滿足條件的。

結果如下:

[nier, sar, juaya, rock, nier]



在這里插入圖片描述



本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/712142.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/712142.shtml
英文地址,請注明出處:http://en.pswp.cn/news/712142.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

python之計算CPI

CPI&#xff0c;即消費者物價指數&#xff08;Consumer Price Index&#xff09;&#xff0c;是一個反映居民家庭一般所購買的消費品和服務項目價格水平變動情況的宏觀經濟指標。它是在特定時段內度量一組代表性消費商品及服務項目的價格水平隨時間而變動的相對數&#xff0c;通…

網絡測試相關

前言 網絡測試通常是指在網絡環境比較復雜&#xff0c;而且有較多限制時&#xff0c;就需要清楚網絡的走向和途徑的節點&#xff0c;便于在出現問題時進行排查和優化網絡性能&#xff0c;相關知識大多是計算機網絡的 測試工具 抓包 wireshark 路由探測 traceroute/tracert 這…

云快充充電樁系統設計書

充電樁系統設計書 一、系統設計概述 隨著新能源汽車市場的快速發展&#xff0c;充電樁作為電動汽車的重要配套設施&#xff0c;其市場需求日益增長。本系統旨在提供一套穩定、高效、易用的充電樁解決方案&#xff0c;以滿足市場上新能源充電樁的主流需求。通過實現云快充V1.6協…

nginx使用詳解--動靜分離

什么是動靜分離&#xff1f; 為了提高網站的響應速度&#xff0c;減輕程序服務器&#xff08;Tomcat&#xff0c;Jboss等&#xff09;的負載&#xff0c;對于靜態資源&#xff0c;如圖片、js、css等文件&#xff0c;可以在反向代理服務器中進行緩存&#xff0c;這樣瀏覽器在請…

如何選擇激活函數?

選擇神經網絡的激活函數是一個重要的決策&#xff0c;因為激活函數的選擇會影響到網絡的收斂速度以及能否有效地訓練。不同的激活函數適用于不同的任務和網絡架構。以下是選擇激活函數時可以考慮的一些指導原則和常用的激活函數&#xff1a; 指導原則 問題類型&#xff1a; 二…

LLM小記

RAG 參考 檢索增強生成(RAG)概念及優化 主流模型 llama系列 1. 背景&#xff1a;模型參數量級的積累&#xff0c;或者訓練數據的增加&#xff0c;哪個對性能提升幫助更大&#xff1f; 最近的 “Training Compute-Optimal Large Language Models” 這篇論文提出一種縮放定…

Java:PDF圖片抽取的兩種方法

圖片和PDF是我們日常生活和工作中經常接觸到的文檔格式。PDF是人們日常使用最多的跨平臺文檔&#xff0c;是一種用獨立于應用程序、硬件、操作系統的方式呈現文檔的文件格式。每個PDF文件包含固定布局的平面文檔的完整描述&#xff0c;包括文本、字形、圖形及其他需要顯示的信息…

如何利用HubSpot海外獲客系統實現海外市場擴張?

在當今全球化的時代&#xff0c;企業面臨著越來越激烈的競爭&#xff0c;而海外市場則被視為獲取更多增長機會的重要途徑之一。針對這一挑戰&#xff0c;企業需要建立一個完整的海外獲客系統&#xff0c;而HubSpot軟件的應用則成為了關鍵。作為HubSpot的合作伙伴&#xff0c;我…

librtmp源碼分析

閱讀了librtmp的源碼&#xff0c;簡單記錄下。 首先補充下AMF格式基本知識 1 AMF格式 AMF是Action Message Format(動作消息格式)的簡寫&#xff0c;它是一種二進制的數據格式。它的設計是為了把actionscript里面的數據(包括Object, Array, Boolean, Number等)序列化成二進制…

jvm 基礎知識和jvm 調優

類裝載分為以下 5 個步驟&#xff1a; 加載&#xff1a;根據查找路徑找到相應的 class 文件然后導入&#xff1b; 檢查&#xff1a;檢查加載的 class 文件的正確性&#xff1b; 準備&#xff1a;給類中的靜態變量分配內存空間&#xff1b; 解析&#xff1a;虛擬機將常量池中的符…

C# 異步返回類型詳解

在現代軟件開發中&#xff0c;異步編程已經成為一種重要的編程范式&#xff0c;尤其是在需要與I/O密集型操作交互的上下文中&#xff0c;比如網絡請求、數據庫操作等。C# 語言提供了強大的異步支持&#xff0c;使得異步編程變得更加簡單和直觀。本文將詳細介紹C#中異步返回類型…

vue3學習 【5】watch的使用

什么是watch 當我們需要根據一個數據的變化來進行一些操作的時候我們需要使用偵聽器&#xff0c;它能夠在響應式數據發生變化的時候觸發提供的回調函數 基礎偵聽 watch 可以偵聽不同的數據源。例如&#xff1a; ref計算屬性響應式對象getter函數多個數據源組層的數據 cons…

20240229金融讀報:央行阿拉善創新融資模式與碳排放權交易條例實施,新春政策聚焦新生產力及金融風險防范

1、人民銀行阿拉善盟分行實例&#xff1a;再貸款產業鏈&#xff08;活體牲畜抵押貸款牲畜死亡率保險&#xff09;、“再貸款新型農牧業經營主體”&#xff08;基礎設施薄弱、產業結構單一&#xff0c;籌集資金創辦嘎查集體經濟股份合作社貸款&#xff09; 2、建設銀行實例&…

oracle11安裝及使用

安裝oracle11 官網下載地址 Oracle Database 11g Release 2 for Microsoft Windows (x64) 官網下載慢可訪問我的資源 也可以網盤獲取 鏈接&#xff1a;https://pan.baidu.com/s/1RDrGkqDA7tfKRnpJXUBMDw 提取碼&#xff1a;z3na 上傳安裝包到服務器 在指定目錄下創建文件…

adb命令

1. 常用命令&#xff1a; adb devices #查看連接設備adb -s cf27456f shell # 指定連接設備使用命令adb install test.apk # 安裝應用adb install -r demo.apk #安裝apk 到sd 卡&#xff1a;adb uninstall cn.com.test.mobile #卸載應用&#xff0c;需要指定包adb uninstall -…

Windows系統x86機器安裝龍芯(loongarch64)3A5000虛擬機系統詳細教程

本次介紹在window系統x86機器上安裝loongarch64系統的詳細教程。 1.安裝環境準備。 首先&#xff0c;你得有臺電腦。 配置別太差&#xff0c;至少4核8G內存&#xff0c;安裝window10或者11都行&#xff08;為啥不能是Window7&#xff0c;你要用也不是不行&#xff0c;你先解決…

leetcode:860.檸檬水找零

題意&#xff1a;按照支付順序&#xff0c;進行支付&#xff0c;能夠正確找零。 解題思路&#xff1a;貪心策略&#xff1a;針對支付20的客人&#xff0c;優先選擇消耗10而不是消耗5&#xff0c;因為5可以用來找零10或20. 代碼實現&#xff1a;有三種情況&#xff08;代表三種…

vscode中使用python的cv2.imread()讀取圖片為None

這是之前的代碼&#xff1a; import cv2img_path c:\\Users\\LENOVO\\Desktop\\QQ截圖20240228234801.jpg print(img_path) print(cv2.__version__)img cv2.imread(img_path) print(img)運行結果&#xff1a; c:\Users\LENOVO\Desktop\QQ截圖20240228234801.jpg 3.4.1 None…

Linux下主線程 return 0 和 pthread_exit(NULL) 的區別

1. 背景 在Linux環境下&#xff0c;主線程以return 0結束時&#xff0c;程序會在主線程運行完畢后結束。而當主線程以pthread_exit(NULL)作為返回值時&#xff0c;主線程會等待子線程結束后才會退出程序。本文將詳細探討這兩種方式的區別&#xff0c;并提供相應的代碼示例。 …

String類的使用

String常用的構造方法 String的源碼 內部是一個數組和hash值&#xff0c;涉及到常量池后續補充&#xff08;常量池&#xff1a;存儲相同的字符時只會存儲一租&#xff09; String的比較 equals()與&#xff1a;String里面為我們提供了許多方法&#xff0c;可直接調用&#xf…