List list = ...;
for(Iterator iter = list.iterator(); iter.hasNext();) {
Object obj = iter.next();
...
if(***) {
list.remove(obj);
}
}
首先这样做是不对的,会抛出 ConcurrentModificationException。
下面来看一下源码
public interface Iterator<E> {
boolean hasNext();
E next();
void remove(); // 这里一个 remove 操作
}
public interface Collection<E> extends Iterable<E> {
...
Iterator<E> iterator();
boolean add(E o);
boolean remove(Object o); // 这里一个 remove 操作
...
}
我们注意到上面有两个remove
操作,一个带参数,一个不带参数
接下来来看看AbstractList
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
//AbstractCollection和List都继承了Collection
protected transient int modCount = 0;
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> { //内部类Itr
int cursor = 0;
int lastRet = -1;
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); //执行remove对象的操作
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount; //重新设置了expectedModCount的值,避免了ConcurrentModificationException的产生
} catch(IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount) //当expectedModCount和modCount不相等时,就抛出ConcurrentModificationException
throw new ConcurrentModificationException();
}
}
}
我们调用iterator()
这个方法的时候返回的就是 Itr
这个内部类,而它的 remove(Object o)
调用的是实际是它的父类AbstractCollection
的方法
public boolean remove(Object o) {
Iterator<E> it = iterator();
if (o==null) {
while (it.hasNext()) {
if (it.next()==null) {
it.remove();
return true;
}
}
} else {
while (it.hasNext()) {
if (o.equals(it.next())) {
it.remove();
return true;
}
}
}
return false;
}
其实这个实现是没有问题的,但是在ArrayList
中重写了这个方法,实现如下:
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;
}
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; // clear to let GC do its work
}
我们看到十几的remove 操作是调用的 fastRemove()
, 先是让 modCount 自增,然后将这个索引后面的数据全部向前移动一个位置,并将最后一个置空。而我们的 next()
方法总是要检查
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
它们的不同步,导致每次check 的时候都会抛出异常。
modCount 和 expectedModCount 分别代表 修改次数和期待的修改次数。
因为有的容器(如:HashMap、ArrayList)不是线程安全的,Iterator 被创建后本身有一个Cursor索引,在使用迭代器的时候如果其他线程修改了容器中的内容,但是这个Cursor不会改变,它们指向的内容没有同步改变,索引往下移动的时候会出现找不到对象的现象,所以应该使用 Iterator 的 remove()
方法
网友评论