dw手機(jī)網(wǎng)站建設(shè)500個游戲推廣群
看到這么標(biāo)題可能覺得這個真是太easy了,不就remove嗎,分分鐘搞定。
但結(jié)果卻出乎意料,下面我們來j簡單說說list刪除數(shù)據(jù)可能遇到的坑:
先說明我們可能會遇到的兩個問題:
1.java.lang.IndexOutOfBoundsException(索引越界)
2.java.util.ConcurrentModificationException(并發(fā)修改異常)
開始測試:
首先初始化一個List<Map<String,String>>
package test02;import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;/** List<Map<String,String>> 刪除元素常見的誤區(qū)* */public class Test09 {public static void main(String[] args) {List<Map<String, String>> list = new ArrayList<Map<String, String>>();Map<String, String> map1=new HashMap<>(); Map<String, String> map2=new HashMap<>();Map<String, String> map3=new HashMap<>();Map<String, String> map4=new HashMap<>();map1.put("key","張三");map1.put("value","20");map2.put("key","李四");map2.put("value","25");map3.put("key","王五");map3.put("value","30");map4.put("key","張三");map4.put("value","35");list.add(map1);list.add(map2);list.add(map3);list.add(map4); for (int i = 0; i < list.size(); i++) {System.out.println("初始化遍歷:"+list.get(i));} }
}
需求:刪除這個list里面,key為張三的數(shù)據(jù);
方式一:for i 循環(huán) 通過索引使用:list.remove(i)刪除;
for (int i = 0; i < list.size(); i++) {System.out.println("i:"+i);System.out.println("len:"+list.size());String a = String.valueOf(list.get(i).get("key"));if(a.equals("張三")) {list.remove(i);System.out.println("第"+i+"次循環(huán)刪除成功");System.out.println("刪除后長度:"+list.size());}System.out.println("方式一遍歷:"+list.get(i));}
出現(xiàn)異常報錯:java.lang.IndexOutOfBoundsException(索引越界)
原因:每次循環(huán)刪除元素之后,初始長度已發(fā)生變化,在最后一次循環(huán)出現(xiàn)越界
打印輸出分析:
i:0
len:4
第0次循環(huán)刪除成功
刪除后長度:3
方式一遍歷:{value=25, key=李四}
i:1
len:3
方式一遍歷:{value=30, key=王五}
i:2
len:3
第2次循環(huán)刪除成功
刪除后長度:2
Exception in thread “main” java.lang.IndexOutOfBoundsException: Index 2 out of bounds for length 2
at java.base/jdk.internal.util.Preconditions.outOfBounds(Preconditions.java:64)
at java.base/jdk.internal.util.Preconditions.outOfBoundsCheckIndex(Preconditions.java:70)
at java.base/jdk.internal.util.Preconditions.checkIndex(Preconditions.java:266)
at java.base/java.util.Objects.checkIndex(Objects.java:359)
at java.base/java.util.ArrayList.get(ArrayList.java:427)
at test01/test02.Test09.main(Test09.java:62)
方式二:foreach循環(huán):list.remove(map)刪除
for(Map<String, String> map :list) {String a = String.valueOf(map.get("key"));if(a.equals("張三")) {list.remove(map);}System.out.println("方式二遍歷:"+map);}
出現(xiàn)異常報錯:java.util.ConcurrentModificationException(并發(fā)修改異常)
通過源碼分析:發(fā)現(xiàn)在 next、remove方法中都會調(diào)用checkForComodification 方法,
該方法的 作用是判斷 modCount != expectedModCount是否相等,
如果不相等則拋出ConcurrentModificationException異常;
當(dāng)我們調(diào)用 list.remove(item)時,對 list 對象的 modCount 值進(jìn)行了修改;
而 list 對象的迭代器的 expectedModCount 值未進(jìn)行修改;
所以就拋出ConcurrentModificationException異常!
private class Itr implements Iterator {
int cursor; // 要返回的下一個元素的索引
int lastRet = -1; // 返回的最后一個元素的索引;如果沒有就返回-1
int expectedModCount = modCount;
public boolean hasNext() {return cursor != size;}@SuppressWarnings("unchecked")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];}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();}}final void checkForComodification() {if (modCount != expectedModCount)throw new ConcurrentModificationException();}
}
方式三:Stream流:filter(推薦)
list = list.stream().filter(e -> (!e.get("key").equals("張三"))).collect(Collectors.toList());for (int i = 0; i < list.size(); i++) {System.out.println("方式三遍歷:"+list.get(i));}
輸出:
方式三遍歷:{value=25, key=李四}
方式三遍歷:{value=30, key=王五}
方式四:迭代器iterator的remove方法(使用更加靈活)
Iterator<Map<String, String>> iterator = list.iterator();while (iterator.hasNext()){Map<String, String> next = iterator.next();String key = String.valueOf(next.get(("key")));if(key.equals("張三")){iterator.remove();}}for (Map<String, String> map : list) {System.out.println("方式四遍歷:"+map);}
輸出:
方式四遍歷:{value=25, key=李四}
方式四遍歷:{value=30, key=王五}
簡單總結(jié):
1、用for循環(huán)遍歷List刪除元素時,需要注意索引變化(左移或右移)的問題(不推薦)。
2、List刪除元素時,默認(rèn)按索引刪除,而不是對象刪除(不推薦)。
3、List刪除元素時,為避免陷阱,建議使用Stream流的filter方式(推薦)。
3、List刪除元素時,為避免陷阱,建議使用迭代器iterator的remove方式(推薦)。