概述
错误写法示例一:
public static
void
remove
(
ArrayList
<
String
>
list
) {
for
(
int
i
=
0
;
i
<
list
.
size
();
i
++
) {
String
s
=
list
.
get
(
i
);
if
(
s
.
equals
(
"bb"
)) {
list
.
remove
(
s
);
}
}
}
错误写法示例二:
public static
void
remove
(
ArrayList
<
String
>
list
) {
for
(
String
s
:
list
) {
if
(
s
.
equals
(
"bb"
)) {
list
.
remove
(
s
);
}
}
}
要分析产生上述错误现象的原因唯有翻一翻
jdk
的
ArrayList
源码,先看下
ArrayList
中的
remove
方法(注
意
ArrayList
中的
remove
有两个同名方法,只是入参不同,这里看的是入参为
Object
的
remove
方法)是
怎么实现的:
public
boolean
remove
(
Object
o
) {
if
(
o
==
null
) {
for
(
int
index
=
0
;
index
<
size
;
index
++
)
if
(
elementData
[
index
]
==
null
) {
fastRemove
(
index
);
return
true
;
}
}
else
{
for
(
int
index
=
0
;
index
<
size
;
index
++
)
if
(
o
.
equals
(
elementData
[
index
])) {
fastRemove
(
index
);
return
true
;
}
}
return
false
;
}
按一般执行路径会走到
else
路径下最终调用
faseRemove
方法:
private
void
fastRemove
(
int
index
) {
modCount
++
;
int
numMoved
=
size
-
index
-
1
;
if
(
numMoved
>
0
)
System
.
arraycopy
(
elementData
,
index
+
1
,
elementData
,
index
,
numMoved
);
elementData
[
--
size
]
=
null
;
// Let gc do its work
}
可以看到会执行
System.arraycopy
方法,导致删除元素时涉及到数组元素的移动。针对错误写法一,在
遍历第二个元素字符串
bb
时因为符合删除条件,所以将该元素从数组中删除,并且将后一个元素移动
(也是字符串
bb
)至当前位置,导致下一次循环遍历时后一个字符串
bb
并没有遍历到,所以无法删除。
针对这种情况可以倒序删除的方式来避免:
public static
void
remove
(
ArrayList
<
String
>
list
) {
for
(
int
i
=
list
.
size
()
-
1
;
i
>=
0
;
i
--
) {
String
s
=
list
.
get
(
i
);
if
(
s
.
equals
(
"bb"
)) {
list
.
remove
(
s
);
}
}
}
因为数组倒序遍历时即使发生元素删除也不影响后序元素遍历。
而错误二产生的原因却是
foreach
写法是对实际的
Iterable
、
hasNext
、
next
方法的简写,问题同样处在
上文的
fastRemove
方法中,可以看到第一行把
modCount
变量的值加一,但在
ArrayList
返回的迭代器
(该代码在其父类
AbstractList
中):
public
Iterator
<
E
>
iterator
() {
return new
Itr
();
}
这里返回的是
AbstractList
类内部的迭代器实现
private class Itr implements Iterator
,看这个类的
next
方法:
public
E
next
() {
checkForComodification
();
try
{
E next
=
get
(
cursor
);
lastRet
=
cursor
++
;
return
next
;
}
catch
(
IndexOutOfBoundsException e
) {
checkForComodification
();
throw new
NoSuchElementException
();
}
}
第一行
checkForComodification
方法:
final
void
checkForComodification
() {
if
(
modCount
!=
expectedModCount
)
throw new
ConcurrentModificationException
();
}
这里会做迭代器内部修改次数检查,因为上面的
remove(Object)
方法把修改了
modCount
的值,所以才
会报出并发修改异常。要避免这种情况的出现则在使用迭代器迭代时(显示或
foreach
的隐式)不要使用
ArrayList
的
remove
,改为用
Iterator
的
remove
即可。
public static
void
remove
(
ArrayList
<
String
>
list
) {
Iterator
<
String
>
it
=
list
.
iterator
();
while
(
it
.
hasNext
()) {
String
s
=
it
.
next
();
if
(
s
.
equals
(
"bb"
)) {
it
.
remove
();
}
}
}
最后
以上就是柔弱冷风为你收集整理的遍历ArrayList时如何正确移除一个元素的全部内容,希望文章能够帮你解决遍历ArrayList时如何正确移除一个元素所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
发表评论 取消回复