美文网首页Java程序员
Collection与Iterator的remove()方法区别

Collection与Iterator的remove()方法区别

作者: 我才不花心 | 来源:发表于2017-04-04 16:43 被阅读0次

    在我的上一篇文章 Java中三种遍历Collection中元素的方法(Iterator、forEach、for循环)对比 中提到Iterator和forEach循环在遍历Collection中元素时最大的差别就是在方法remove()上,由于在Iterator的remove()方法中维护一个标志位,所以删除元素时不会出现异常,所以本篇文章就深入Collection与Iterator的源码看看内部究竟是如何实现的。

    一. Collection及其实现类ArrayList的部分源码

    1.Collection内部源码

    首先我们来看一下Collection内部源码(为方便分析,此处只展示与本篇文章有关的部分):

    public interface Collection<E> extends Iterable<E> {
    
        boolean remove(Object o);
    
        Iterator<E> iterator();
    
        /**
         *  此处省去其他方法定义
         */
    }
    

    可以看到Collection是一个接口,内部定义了remove()iterator()方法。

    2.ArrayList内部源码

    由于Collection接口内部无具体实现,所以我们来看Collection的一个最常用的实现类ArrayList内部源码(为方便分析,此处只展示与本篇文章有关的部分):

    public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {
    
        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
        }
    
    
        /* -----------------------我是便于观察的分割线----------------------- */
    
    
        public Iterator<E> iterator() {
            return new 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();
            }
    
        /**
         *  此处省去其他方法定义
         */
    }
    

    在ArrayList中并没有直接实现Collection接口,而是通过继承AbstractList抽象类,而AbstractList抽象类又继承了AbstractCollection抽象类,最终AbstractCollection抽象类实现Collection接口;所以ArrayList间接实现了Collection接口,有兴趣的大佬可以自己去研究下为什么这样子设计,在这里就不多加讨论。

    可以看到在ArrayList中有实现remove()iterator()方法,并且通过iterator()方法得到的内部类Itr实现了Iterator接口,在Itr内部类中也有实现remove()方法,下面就来具体的探讨其中的区别。

    二. ArrayList的remove()方法分析

    1.remove()方法

    在ArrayList的remove()方法内部的实现主要是通过循环找到元素的下标, 然后调用私有的fastRemove()方法:

        fastRemove(index);
    

    remove()方法没啥好讲的,关键在于调用的fastRemove()方法上。

    2.fastRemove()方法

    fastRemove()方法中会先修改modCount的值,然后将通过复制一个新的数组的方法将原来index位置上的值覆盖掉,最后数组大小减一。我们重点关注fastRemove()方法的第一行代码:

        modCount++;
    

    也就是每次调用remove()方法都会使modCount的值加一。那么modCount变量又是什么呢?

    3.modCount变量

    modCount在ArrayList中没有定义,是在ArrayList的父类AbstractList抽象类中定义的:

    public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    
        protected transient int modCount = 0;
    
        /**
         *  此处省去其他方法定义
         */
    }
    

    modCount的作用是记录操作(添加删除)ArrayList中元素的次数(这个很关键),每次操作ArrayList中元素后就会使modCount加一。

    三. Iterator的remove()方法分析

    看源码可知ArrayList通过iterator()方法得到了一个内部类Itr,这个内部类实现了Iterator接口,我们重点分析内部类Itr中的实现。

    1.expectedModCount 变量

    在内部类Itr中定义了一个变量expectedModCount :

        int expectedModCount = modCount;
    

    expectedModCount 只在new一个Itr对象时初始化为modCount

    2.next()与remove()方法

    在调用Itr对象的next()remove()方法时第一步会先调用checkForComodification()方法。

        checkForComodification();
    

    并且在remove()方法中会调用ArrayList.this.remove(lastRet)方法(也就是具体的ArrayList对象的remove()方法,上面我们讲过,在ArrayList对象的remove()方法中会使得modCount的值加一),然后修改expectedModCount 的值为modCount

    3.checkForComodification()方法

    checkForComodification()会检查expectedModCount与modCount 是否相等,如果不相等就会抛出ConcurrentModificationException异常。

            final void checkForComodification() {
                if (modCount != expectedModCount)
                    throw new ConcurrentModificationException();
            }
    

    四. 总结

    通过上面的分析我们可以得出,Collection与Iterator的remove()方法最大的区别就是:
    Iterator的remove()方法会在删除元素后将modCount 的值赋值给expectedModCount,使其又相等

    1.如果我们在Iterator循环中调用Collection的remove()方法

    public static void display(Collection<Object> collection) {
        Iterator<Object> it = collection.iterator();
    
        // 会抛出ConcurrentModificationException异常
        while(it.hasNext()) {
            Object obj = it.next();
            collection.remove(obj ); 
        }
    }
    

    由于collection.remove(obj )只会删除obj元素后将modCount 的值加一,并不会修改expectedModCount的值,所以当下一次调用it.next()方法时发现modCount != expectedModCount,将抛出ConcurrentModificationException异常。

    2.如果我们在Iterator循环中调用Iterator的remove()方法

    public static void display(Collection<Object> collection) {
        Iterator<Object> it = collection.iterator();
        // 正常执行
        while(it.hasNext()) {
            Object obj = it.next();
            it.remove(obj ); 
        }
    }
    

    由于it.remove(obj )会在删除obj元素后将modCount 的值加一,并将expectedModCount重新赋值为modCount ,使其相等,所以当下一次调用it.next()方法时发现modCount == expectedModCount,正常执行。

    相关文章

      网友评论

        本文标题:Collection与Iterator的remove()方法区别

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