JAVA学习-Iterator迭代器详解

2017-05-16  本文已影响716人  遇见技术

1.定义

Iterator的定义为:对Collection进行迭代的迭代器,Iterator取代了Java Collection Framework中的 Enumeration。Iterator与Enumeration主要有两点不一样:

1.迭代器允许在调用者从集合中删除元素

2.迭代器的方法名有所改进

以上是Java Api对Iterator的介绍,该接口主要定义了如下代码的规则

public interface Iterator<E> {
    //是否有下一个元素
    boolean hasNext();
    //获取当前元素
    E next();
    //删除元素
    void remove();
}

在使用Iterator时,我遇到如下两个问题,

1.Iterator是什么,这便是上述介绍的内容

2.为何使用Iterator,换句话说Iterator能做什么,下面将通过ArrayList中的Iterator的实现介绍Iterator的原理及应用

2.原理及实现

在ArrayList中有两个内部类,都实现了Iterator接口,分别为Itr与ListItr,如下首先介绍Itr的源码

private class Itr implements Iterator<E> {
    //游标记录当前索引的位置,是从0开始的
    int cursor;       // index of next element to return
    int lastRet = -1; // index of last element returned; -1 if no such
    int expectedModCount = modCount;//记录元素修改记录,若在迭代List时,modCount发生变化将会抛出ConcurrentModificationException异常

    //判断游标是否到达最后的位置,若没有表示还有元素,若有则没有元素了
    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        //校验是否被修改(其实这里存在多线程问题,所以说ArrayList不是线程安全的)
        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;//游标向后移动1
        return (E) elementData[lastRet = i];//返回值并且赋值
    }

    public void remove() {
        //lastRet初始值为-1,若需要调用remove方法,则bi
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            //删除lastRet索引处的元素
            ArrayList.this.remove(lastRet);
            
            //游标前移,由于是删除remove方法删除的是lastRet位置的元素则游标需要前移才能保证可以遍历到所有的元素
            cursor = lastRet;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    //检查列表是否修改,若修改则抛出异常
    final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

通过源码可以发现迭代器的方式遍历列表是不需要知道list的长度的,只需要判断是否具有元素就可以进行遍历了,如下代码所示

//1.获取迭代器
Iterator<String> ite = arrayList.iterator();
//2.判断是否遍历结束
while (ite.hasNext()){
     System.out.println(ite.next());
}

在ArryList详解中我有提到使用Iterator可以实现在对List进行遍历是进行删除操作,具体代码如下所示

Iterator<String> ite = arrayList.listIterator();
while (ite.hasNext()){
    if(ite.next() == "a")
        ite.remove();
}
System.out.println(arrayList);

这里需要思考一个问题,ite.remove()方法调用的实际上是ArrayList的remove(index)方法,也改变了数组的长度大小并且数组元素也发生了变化,那为什么使用这种方式可以得到正常的结果?

其实这需要从迭代器的remove的源码进行分析, 下面举例分析,list中的初始值为[a,b,c,d],当lastRet=0时,此时cursor=1,首先执行A处的代码,此时list变成[b,c,d],若要重新遍历那就要把cursor变成上次删除出的元素的索引,即执行B处的代码,这里便是迭代器循环删除元素时可以得到正常的结果原因,利用游标的方式在删除元素后移动游标达到可以遍历到所有元素的目的

    ArrayList.this.remove(lastRet);//A
    cursor = lastRet;//B
    lastRet = -1;//C
    expectedModCount = modCount;//D

3.总结

本文主要针对Iterator一个基本的实现做了相关的介绍,后续还会根据深入的去理解其中的原理,如有问题希望指正。

上一篇下一篇

猜你喜欢

热点阅读