Java的List在刪除元素時,一般會用list.remove(o)/remove(i)方法。在使用時,容易觸碰陷阱,得到意想不到的結(jié)果。總結(jié)以往經(jīng)驗,記錄下來與大家分享。
首先初始化List,代碼如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
|
package com.cicc.am.test; import java.util.ArrayList; import java.util.List; public class ListTest { public static void main(String[] args) { List<Integer> list= new ArrayList<Integer>(); list.add( 1 ); list.add( 2 ); list.add( 3 ); list.add( 3 ); list.add( 4 ); System.out.println(list); } } |
輸出結(jié)果為[1, 2, 3, 3, 4]
1、普通for循環(huán)遍歷List刪除指定元素--錯誤!!!
1
2
3
4
|
for ( int i= 0 ;i<list.size();i++){ if (list.get(i)== 3 ) list.remove(i); } System.out.println(list); |
輸出結(jié)果:[1, 2, 3, 4]
為什么元素3只刪除了一個?本以為這代碼再簡單不過,可還是掉入了陷阱里,上面的代碼這樣寫的話,元素3是過濾不完的。只要list中有相鄰2個相同的元素,就過濾不完。List調(diào)用remove(index)方法后,會移除index位置上的元素,index之后的元素就全部依次左移,即索引依次-1要保證能操作所有的數(shù)據(jù),需要把index-1,否則原來索引為index+1的元素就無法遍歷到(因為原來索引為index+1的數(shù)據(jù),在執(zhí)行移除操作后,索引變成index了,如果沒有index-1的操作,就不會遍歷到該元素,而是遍歷該元素的下一個元素)。
如果這樣,刪除元素后同步調(diào)整索引或者倒序遍歷刪除元素,是否可行呢?
2、for循環(huán)遍歷List刪除元素時,讓索引同步調(diào)整--正確!
1
2
3
4
|
for ( int i= 0 ;i<list.size();i++){ if (list.get(i)== 3 ) list.remove(i--); } System.out.println(list); |
輸出結(jié)果:[1, 2, 4]
3、倒序遍歷List刪除元素--正確!
1
2
3
4
5
6
|
for ( int i=list.size()- 1 ;i>= 0 ;i--){ if (list.get(i)== 3 ){ list.remove(i); } } System.out.println(list); |
輸出結(jié)果:[1, 2, 4]
4、foreach遍歷List刪除元素--錯誤!!!
1
2
3
4
|
for (Integer i:list){ if (i== 3 ) list.remove(i); } System.out.println(list); |
拋出異常:java.util.ConcurrentModificationException
foreach 寫法實際上是對的 Iterable、hasNext、next方法的簡寫。因此從List.iterator()源碼著手分析,跟蹤iterator()方法,該方法返回了 Itr 迭代器對象。
1
2
3
|
public Iterator<E> iterator() { return new Itr(); } |
Itr 類定義如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
|
private class Itr implements Iterator<E> { int cursor; // index of next element to return int lastRet = - 1 ; // index of last element returned; -1 if no such 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(); } } |
通過代碼我們發(fā)現(xiàn) Itr 是 ArrayList 中定義的一個私有內(nèi)部類,在 next、remove方法中都會調(diào)用checkForComodification 方法,該方法的 作用是判斷 modCount != expectedModCount是否相等,如果不相等則拋出ConcurrentModificationException異常。每次正常執(zhí)行 remove 方法后,都會對執(zhí)行expectedModCount = modCount賦值,保證兩個值相等,那么問題基本上已經(jīng)清晰了,在 foreach 循環(huán)中
執(zhí)行 list.remove(item);,對 list 對象的 modCount 值進(jìn)行了修改,而 list 對象的迭代器的 expectedModCount 值未進(jìn)行修改,因此拋出了ConcurrentModificationException異常。
5、迭代刪除List元素--正確!
java中所有的集合對象類型都實現(xiàn)了Iterator接口,遍歷時都可以進(jìn)行迭代:
1
2
3
4
5
6
7
|
Iterator<Integer> it=list.iterator(); while (it.hasNext()){ if (it.next()== 3 ){ it.remove(); } } System.out.println(list); |
輸出結(jié)果:[1, 2, 4]
Iterator.remove() 方法會在刪除當(dāng)前迭代對象的同時,會保留原來元素的索引。所以用迭代刪除元素是最保險的方法,建議大家使用List過程
中需要刪除元素時,使用這種方式。
6、迭代遍歷,用list.remove(i)方法刪除元素--錯誤!!!
1
2
3
4
5
6
7
8
|
Iterator<Integer> it=list.iterator(); while (it.hasNext()){ Integer value=it.next(); if (value== 3 ){ list.remove(value); } } System.out.println(list); |
拋出異常:java.util.ConcurrentModificationException,原理同上述方法4.
7、List刪除元素時,注意Integer類型和int類型的區(qū)別.
上述Integer的list,直接刪除元素2,代碼如下:
1
2
|
list.remove( 2 ); System.out.println(list); |
輸出結(jié)果:[1, 2, 3, 4]
可以看出,List刪除元素時傳入數(shù)字時,默認(rèn)按索引刪除。如果需要刪除Integer對象,調(diào)用remove(object)方法,需要傳入Integer類型,代碼如下:
1
2
|
list.remove( new Integer( 2 )); System.out.println(list); |
輸出結(jié)果:[1, 3, 3, 4]
總結(jié):
1、用for循環(huán)遍歷List刪除元素時,需要注意索引會左移的問題。
2、List刪除元素時,為避免陷阱,建議使用迭代器iterator的remove方式。
3、List刪除元素時,默認(rèn)按索引刪除,而不是對象刪除。
到此這篇關(guān)于Java List的remove()方法踩坑的文章就介紹到這了,更多相關(guān)Java List remove()內(nèi)容請搜索服務(wù)器之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持服務(wù)器之家!
原文鏈接:https://blog.csdn.net/pelifymeng2/article/details/78085836