java 如何在list中刪除我指定的對(duì)象
遍歷list,刪除指定對(duì)象的三種方式
1、再定義一個(gè)List,用來保存需要?jiǎng)h除的對(duì)象
修改部分代碼:
List<User> userRemove = new ArrayList<User>(); //找出要?jiǎng)h除的用戶 System.err.println("要?jiǎng)h除的用戶:"); for (User result : list) { if (result.getId() == 1 || result.getId() == 3) { userRemove.add(result); System.err.println("id:" + result.getId() + "\tname:" + result.getName()); } } list.removeAll(userRemove); //剩下的用戶 System.err.println("剩下的用戶:"); for (User result : list) { System.err.println("id:" + result.getId() + "\tname:" + result.getName()); }
2、不用for-each循環(huán),使用倒序循環(huán)刪除
for(int i=list.size()-1;i>=0;i--) { User result = list.get(i); if (result.getId() == 3) { list.remove(result); System.err.println("id: " + result.getId() + "\tname: " + result.getName()); } }
3、用迭代器刪除
Iterator<User> it = list.iterator(); while (it.hasNext()) { User userObj = it.next(); if (userObj.getId() == 3) { it.remove(); } } //剩下的用戶 System.err.println("剩下的用戶:"); for (User result : list) { System.err.println("id:" + result.getId() + "\tname:" + result.getName()); }
PS: 用for-each遍歷 實(shí)際上使用的是Iterator迭代器
Iterator的工作機(jī)制
Iterator是工作在一個(gè)獨(dú)立的線程中,并且擁有一個(gè) mutex鎖,就是說Iterator在工作的時(shí)候,是不允許被迭代的對(duì)象被改變的。
Iterator被創(chuàng)建的時(shí)候,建立了一個(gè)內(nèi)存索引表(單鏈表),這 個(gè)索引表指向原來的對(duì)象,當(dāng)原來的對(duì)象數(shù)量改變的時(shí)候,這個(gè)索引表的內(nèi)容沒有同步改變,所以當(dāng)索引指針往下移動(dòng)的時(shí)候,便找不到要迭代的對(duì)象,于是產(chǎn)生錯(cuò) 誤。
List、Set等是動(dòng)態(tài)的,可變對(duì)象數(shù)量的數(shù)據(jù)結(jié)構(gòu),但是Iterator則是單向不可變,只能順序讀取,不能逆序操作的數(shù)據(jù)結(jié)構(gòu),當(dāng) Iterator指向的原始數(shù)據(jù)發(fā)生變化時(shí),Iterator自己就迷失了方向。
三種方式 方便以后學(xué)習(xí) !
List集合刪除元素的正確姿勢(shì)
在閱讀阿里巴巴規(guī)約的時(shí)候發(fā)現(xiàn)有一條規(guī)約是關(guān)于List的【不要在foreach里面進(jìn)行元素的remove/add操作,remove請(qǐng)使用Iterator方式】。然后想起以前自己做項(xiàng)目的時(shí)候刪除某一元素的邏輯報(bào)下標(biāo)越界錯(cuò)誤,那時(shí)候記得處理是用一新的List進(jìn)行存儲(chǔ),然后整體從原List移除所有符合規(guī)則的元素,現(xiàn)在做一總結(jié)。用這個(gè)Iterator主要可以避免下標(biāo)越界或者遍歷是漏掉符合規(guī)則的下一個(gè)數(shù)據(jù)。
先拉出來正確刪除元素的姿勢(shì)。正確姿勢(shì)是利用Iterator的remove方法。具體操作如下:
public static void main(String[] args) { List<Integer> lists = new ArrayList<Integer>(); lists.add(1); lists.add(2); lists.add(3); lists.add(4); lists.add(5); Iterator<Integer> iterator = lists.iterator(); while (iterator.hasNext()){ Integer obj = iterator.next(); if(3==obj || 4==obj){ iterator.remove(); } } System.out.println(lists); }
附上自己比較笨的操作方式:
public static void main(String[] args){ List<Integer> lists = new ArrayList<Integer>(); List<Integer> listscopy = new ArrayList<>(); lists.add(1); lists.add(2); lists.add(3); lists.add(4); lists.add(5); int size = lists.size(); for (int i = 0; i < lists.size(); i++) { if(3==lists.get(i) || 4 == lists.get(i)){ listscopy.add(lists.get(i)); } } lists.removeAll(listscopy); System.out.println(lists); }
常用的錯(cuò)誤方式有以下三種
第一種方式,多出現(xiàn)下標(biāo)越界問題。這個(gè)主要原因是因?yàn)槲覀冊(cè)谘h(huán)遍歷時(shí),將我們的長(zhǎng)度進(jìn)行定值確定。而忽略掉在滿足條件時(shí),list的長(zhǎng)度是減少的。
public static void main(String[] args) { List<Integer> lists = new ArrayList<Integer>(); lists.add(1); lists.add(2); lists.add(3); lists.add(4); lists.add(5); int size = lists.size(); // 定長(zhǎng)設(shè)置,會(huì)造成元素下標(biāo)越界,如果將for中變量直接換成lists.size()可以嗎? for (int i = 0; i < size; i++) { if(3==lists.get(i) || 4 == lists.get(i)){ lists.remove(i); //lists.remove(lists.get(i)); } } System.out.println(lists); }
第二種方式也就是上面的注釋所說,既然會(huì)出現(xiàn)下標(biāo)越界,那我就利用動(dòng)態(tài)的大小不就可以了,但是利用這種方法會(huì)產(chǎn)生另外一種情況,就是會(huì)忽略掉remove元素后面的一個(gè)元素,這個(gè)是因?yàn)閯h除符合規(guī)則的元素后,list長(zhǎng)度減一,而同時(shí)后面的元素往前補(bǔ)一位,造成當(dāng)前i值下對(duì)應(yīng)兩個(gè)元素。
public static void main(String[] args) { List<Integer> lists = new ArrayList<Integer>(); lists.add(1); lists.add(2); lists.add(3); lists.add(4); lists.add(5); int size = lists.size(); for (int i = 0; i < lists.size(); i++) { if(3==lists.get(i) || 4 == lists.get(i)){ lists.remove(i); //lists.remove(lists.get(i)); } } System.out.println(lists); // 打印[1, 2, 4, 5] 此時(shí)會(huì)將4忽略掉 }
第三種方式是利用增強(qiáng)FOR循環(huán)造成的。這種方式會(huì)直接給報(bào)錯(cuò):
ERRORInfo: Exception in thread "main" java.util.ConcurrentModificationException at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901) at java.util.ArrayList$Itr.next(ArrayList.java:851) public static void main(String[] args) { List<Integer> lists = new ArrayList<Integer>(); lists.add(1); lists.add(2); lists.add(3); lists.add(4); lists.add(5); for (Integer i : lists){ if(3==i || 4==i){ lists.remove(i); } } System.out.println(lists); }
這種情況下。主要是集合遍歷是使用Iterator, Iterator是工作在一個(gè)獨(dú)立的線程中,并且擁有一個(gè)互斥鎖。Iterator 被創(chuàng)建之后會(huì)建立一個(gè)指向原來對(duì)象的單鏈索引表,當(dāng)原來的對(duì)象數(shù)量發(fā)生變化時(shí),這個(gè)索引表的內(nèi)容不會(huì)同步改變,所以當(dāng)索引指針往后移動(dòng)的時(shí)候就找不到要迭代的對(duì)象,所以按照 fail-fast原則 Iterator 會(huì)馬上拋出java.util.ConcurrentModificationException 異常。所以 Iterator 在工作的時(shí)候是不允許被迭代的對(duì)象被改變的。
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。
相關(guān)文章
Java日期時(shí)間與正則表達(dá)式超詳細(xì)整理(適合新手入門)
如果使用得當(dāng),正則表達(dá)式是匹配各種模式的強(qiáng)大工具,下面這篇文章主要給大家介紹了關(guān)于Java日期時(shí)間與正則表達(dá)式超詳細(xì)整理的相關(guān)資料,本文非常適合新手入門,需要的朋友可以參考下2023-04-04maven項(xiàng)目install時(shí)忽略執(zhí)行test方法的總結(jié)
這篇文章主要介紹了maven項(xiàng)目install時(shí)忽略執(zhí)行test方法的總結(jié),具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Java調(diào)用windows系統(tǒng)的CMD命令并啟動(dòng)新程序
本文教你如何使用java程序調(diào)用windows系統(tǒng)的CMD命令啟動(dòng)新程序方法,需要的朋友可以參考下2023-05-05