今天同事写了几行类似这样的代码:
1
2
3
4
5
6
7
8
9
10
11
12
|
public
static
void
main(String args[]) {
List<String> famous =
new
ArrayList<String>();
famous.add(
"liudehua"
);
famous.add(
"madehua"
);
famous.add(
"liushishi"
);
famous.add(
"tangwei"
);
for
(String s : famous) {
if
(s.equals(
"madehua"
)) {
famous.remove(s);
}
}
}
|
运行出异常:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.AbstractList$Itr.checkForComodification(AbstractList.java:372)
at java.util.AbstractList$Itr.next(AbstractList.java:343)
at com.bes.Test.main(Test.java:15)
Java新手最容易犯的错误,对JAVA集合进行遍历删除时务必要用迭代器。切记。
其实对于如上for循环,运行过程中还是转换成了如下代码:
1
2
3
4
5
6
|
for
(Iterator<String> it = famous.iterator();it.hasNext();){
String s = it.next();
if
(s.equals(
"madehua"
)){
famous.remove(s);
}
}
|
仍然采用的是迭代器,但删除操作却用了错误的方法。如将famous.remove(s)改成it.remove()
则运行正常,结果也无误。
当然如果改成:
1
2
3
4
5
6
|
for
(
int
i =
0
; i < famous.size(); i++) {
String s = famous.get(i);
if
(s.equals(
"madehua"
)) {
famous.remove(s);
}
}
|
这种方法,也是可以完成功能,但一般也不这么写。
为什么用了迭代码器就不能采用famous.remove(s)操作? 这种因为ArrayList与Iterator混合使用时会导致各自的状态出现不一样,最终出现异常。
我们看一下ArrayList中的Iterator实现:
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
43
44
45
46
47
48
49
50
|
private
class
Itr
implements
Iterator<E> {
/**
* Index of element to be returned by subsequent call to next.
*/
int
cursor =
0
;
/**
* Index of element returned by most recent call to next or
* previous. Reset to -1 if this element is deleted by a call
* to remove.
*/
int
lastRet = -
1
;
/**
* The modCount value that the iterator believes that the backing
* List should have. If this expectation is violated, the iterator
* has detected concurrent modification.
*/
int
expectedModCount = modCount;
public
boolean
hasNext() {
return
cursor != size();
}
public
E next() {
checkForComodification();
try
{
E next = get(cursor);
lastRet = cursor++;
return
next;
}
catch
(IndexOutOfBoundsException e) {
checkForComodification();
throw
new
NoSuchElementException();
}
}
public
void
remove() {
if
(lastRet == -
1
)
throw
new
IllegalStateException();
checkForComodification();
try
{
AbstractList.
this
.remove(lastRet);
if
(lastRet < cursor)
cursor--;
lastRet = -
1
;
expectedModCount = modCount;
}
catch
(IndexOutOfBoundsException e) {
throw
new
ConcurrentModificationException();
}
}
final
void
checkForComodification() {
if
(modCount != expectedModCount)
throw
new
ConcurrentModificationException();
}
}
|
基本上ArrayList采用size属性来维护自已的状态,而Iterator采用cursor来来维护自已的状态。
当size出现变化时,cursor并不一定能够得到同步,除非这种变化是Iterator主动导致的。
从上面的代码可以看到当Iterator.remove方法导致ArrayList列表发生变化时,他会更新cursor来同步这一变化。但其他方式导致的ArrayList变化,Iterator是无法感知的。ArrayList自然也不会主动通知Iterator们,那将是一个繁重的工作。Iterator到底还是做了努力:为了防止状态不一致可能引发的无法设想的后果,Iterator会经常做checkForComodification检查,以防有变。如果有变,则以异常抛出,所以就出现了上面的异常。
本文出自 “天下无贼” 博客,请务必保留此出处http://guojuanjun.blog.51cto.com/277646/1348450