Java List的remove()方法陷阱
Java的List在刪除元素時,一般會用list.remove(o)/remove(i)方法。在使用時,容易觸碰陷阱,得到意想不到的結果。總結以往經驗,記錄下來與大家分享。
首先初始化List,程式碼如下:
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
)
;
}
}
輸出結果為
[1, 2, 3, 3, 4]
1、普通for迴圈遍歷List刪除指定元素--錯誤!!!
for
(int i
=
0
;i
<list
.
size
(
)
;i
++
)
{
if
(list
.
get
(i
)
==
3
) list
.
remove
(i
)
;
}
System
.out
.
println
(list
)
;
輸出結果:
[1, 2, 3, 4]
為什麼元素3只刪除了一個?本以為這程式碼再簡單不過,可還是掉入了陷阱裡,上面的程式碼這樣寫的話,元素3是過濾不完的。只要list中有相鄰2個相同的元素,就過濾不完。List呼叫remove(index)方法後,會移除index位置上的元素,index之後的元素就全部依次左移,即索引依次-1要保證能操作所有的資料,需要把index-1,否則原來索引為index+1的元素就無法遍歷到(因為原來索引為index+1的資料,在執行移除操作後,索引變成index了,如果沒有index-1的操作,就不會遍歷到該元素,而是遍歷該元素的下一個元素)。
如果這樣,刪除元素後同步調整索引或者倒序遍歷刪除元素,是否可行呢?
2、for迴圈遍歷List刪除元素時,讓索引同步調整--正確!
for
(int i
=
0
;i
<list
.
size
(
)
;i
++
)
{
if
(list
.
get
(i
)
==
3
) list
.
remove
(i
--
)
;
}
System
.out
.
println
(list
)
;
輸出結果:
[1, 2, 4]
3、倒序遍歷List刪除元素--正確!
for
(int i
=list
.
size
(
)
-
1
;i
>=
0
;i
--
)
{
if
(list
.
get
(i
)
==
3
)
{
list
.
remove
(i
)
;
}
}
System
.out
.
println
(list
)
;
輸出結果:
[1, 2, 4]
4、foreach遍歷List刪除元素--錯誤!!!
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 迭代器物件。
public Iterator
<
E
>
iterator
(
)
{
return
new
Itr
(
)
;
}
Itr 類定義如下:
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
(
)
;
}
}
通過程式碼我們發現 Itr 是 ArrayList 中定義的一個私有內部類,在 next、remove方法中都會呼叫checkForComodification 方法,該方法的 作用是判斷 modCount != expectedModCount是否相等,如果不相等則丟擲ConcurrentModificationException異常。每次正常執行 remove 方法後,都會對執行expectedModCount = modCount賦值,保證兩個值相等,那麼問題基本上已經清晰了,在 foreach 迴圈中
執行 list.remove(item);,對 list 物件的 modCount 值進行了修改,而 list 物件的迭代器的 expectedModCount 值未進行修改,因此丟擲了ConcurrentModificationException異常。
5、迭代刪除List元素--正確!
java中所有的集合物件型別都實現了Iterator介面,遍歷時都可以進行迭代:
Iterator
<Integer
> it
=list
.
iterator
(
)
;
while
(it
.
hasNext
(
)
)
{
if
(it
.
next
(
)
==
3
)
{
it
.
remove
(
)
;
}
}
System
.out
.
println
(list
)
;
輸出結果:
[1, 2, 4]
Iterator.remove() 方法會在刪除當前迭代物件的同時,會保留原來元素的索引。所以用迭代刪除元素是最保險的方法,建議大家使用List過程
中需要刪除元素時,使用這種方式。
6、迭代遍歷,用list.remove(i)方法刪除元素--錯誤!!!
//java學習交流:737251827 進入可領取學習資源及對十年開發經驗大佬提問,免費解答!
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型別的區別.
上述Integer的list,直接刪除元素2,程式碼如下:
list
.
remove
(
2
)
;
System
.out
.
println
(list
)
;
輸出結果:
[1, 2, 3, 4]
可以看出,List刪除元素時傳入數字時,預設按索引刪除。如果需要刪除Integer物件,呼叫remove(object)方法,需要傳入Integer型別,程式碼如下:
list
.
remove
(
new
Integer
(
2
)
)
;
System
.out
.
println
(list
)
;
輸出結果:
[1, 3, 3, 4]
總結:
1、用for迴圈遍歷List刪除元素時,需要注意索引會左移的問題。
2、List刪除元素時,為避免陷阱,建議使用迭代器iterator的remove方式。
3、List刪除元素時,預設按索引刪除,而不是物件刪除。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70010294/viewspace-2845095/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- Remove-duplicates-from-sorted-listREM
- 多執行緒下使用List中的subList和remove方法產生的 java.util.ConcurrentModificationException 異常執行緒REMJavaException
- Leetcode 203. Remove Linked List ElementsLeetCodeREM
- LeetCode | 203. Remove Linked List ElementsLeetCodeREM
- Leetcode 19 Remove Nth Node From End of ListLeetCodeREM
- [LeetCode] 2487. Remove Nodes From Linked ListLeetCodeREM
- LeetCode 83. Remove Duplicates from Sorted ListLeetCodeREM
- 【程式碼優化】List.remove() 剖析優化REM
- LeetCode Remove Nth Node From End of List(019)解法總結LeetCodeREM
- Java 中比較 BigDecimal 的陷阱JavaDecimal
- 在 Java 中初始化 List 的五種方法Java
- Leetcode(Python3) 19. Remove Nth Node From End of ListLeetCodePythonREM
- jQuery之empty()與remove()方法jQueryREM
- Python List 列表list()方法Python
- java之深入理解List.subList()方法Java
- Lambda 表示式遍歷集合時用remove方法刪除list集合中滿足條件的元素問題REM
- 瞬間教你學會使用java中list的retainAll方法JavaAI
- Python List 列表list()方法分享Python
- 執行ArrayList的remove(object)方法拋異常?REMObject
- Java 8並行流的效能陷阱Java並行
- LeetCode 83.Remove Duplicates from Sorted List(從已排序連結串列中除去重複) Easy/Linked ListLeetCodeREM排序
- java集合-ListJava
- Java 的 Collection 與 List 集合Java
- js中dom節點刪除remove方法JSREM
- java解析json listJavaJSON
- 瘋狂java07--物件導向的陷阱Java物件
- java陷阱之不可忽視的預設配置Java
- Java碼農必須掌握的迴圈刪除List元素的正確方法Java
- 資料結構與演算法 | Leetcode 19. Remove Nth Node From End of List資料結構演算法LeetCodeREM
- C++中list的使用方法及常用list操作總結C++
- java原始碼-java.util.ListJava原始碼
- python中list的各種方法使用Python
- 陣列結構 list () 的替代方法陣列
- Python中清空list的幾種方法Python
- Java 中,Arrays 轉 List 的那些坑Java
- 對Java list集合的新認知Java
- java中list的常見問題。Java
- java中的List介面(ArrayList、Vector、LinkedList)Java