美文网首页
Iterator 的移除操作

Iterator 的移除操作

作者: 莫库施勒 | 来源:发表于2019-06-11 15:52 被阅读0次
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() 方法

相关文章

网友评论

      本文标题:Iterator 的移除操作

      本文链接:https://www.haomeiwen.com/subject/jnpqfctx.html