ArrayList源码-迭代器

喜你入骨 提交于 2020-08-17 02:45:09

        上一篇笔记主要是一些常用方法和相关联的方法的总结,这一篇主要记录一下迭代器相关逻辑。

        数组列表的迭代器是由于集合接口继承迭代器接口决定的,迭代器接口这里就不再重复了,有记录:迭代器接口。集合类都会有返回迭代器的方法:

/**
 * 以正确的顺序返回此列表中元素的迭代器。
 * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
 * @return an iterator over the elements in this list in proper sequence
 */
public Iterator<E> iterator() {
    return new Itr();//创建一个对象,具体逻辑在内部类中
}

        迭代器方法的具体实现在 AbstractList 类中已经给出了,不过其子类型的实现细节,也就是 Itr 这个内部类会有所不同。

/**
 * An optimized version of AbstractList.Itr 相比较 AbstractList.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 上一个返回元素的下标,如果这个元素值是 -1
    int expectedModCount = modCount;

    Itr() {}//无参构造方法

    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; //cursor + 1
        return (E) elementData[lastRet = i]; //修改lastRet ,并返回该位置元素
    }

    public void remove() {
        if (lastRet < 0)//调用next 方法之前调用该方法
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.remove(lastRet);
            cursor = lastRet;
            lastRet = -1;//被删了相当于上一个没有返回,接下去的遍历修改方式是 lastRet = i = cursor 没有影响
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
//这个方法暂时不看
    @Override
    @SuppressWarnings("unchecked")
    public void forEachRemaining(Consumer<? super E> consumer) {
        Objects.requireNonNull(consumer);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i >= size) {
            return;
        }
        final Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length) {
            throw new ConcurrentModificationException();
        }
        while (i != size && modCount == expectedModCount) {
            consumer.accept((E) elementData[i++]);
        }
        // update once at end of iteration to reduce heap write traffic
        cursor = i;
        lastRet = i - 1;
        checkForComodification();
    }

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

        由以上代码可知,最重要的就是理解两个属性:cursor 和 lastRet ,两个方法:next() 和 remove()。这个迭代器返回元素顺序就是按底层数组下标从小到大依次返回的。删除方法要在next() 方法调用之后调用,而且每次只能调用一次删除方法,因为调用一次后 lastRet =-1,再次调用会抛出异常。

        对于列表还有自己的列表迭代器在List 接口中定义,在 AbstractList 中提供了实现细节。看一下数组列表中的具体实现逻辑。

/**
 * 从列表中的指定位置开始,以适当的顺序返回在此列表中的元素上的列表迭代器。
 * 指定的索引指示初始调用{@link ListIterator#next next}将返回的第一个元素。
 * 初次调用{@link ListIterator#previous previous}将返回具有指定索引减一的元素。
 * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
 * @throws IndexOutOfBoundsException {@inheritDoc}
 */
public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}

/**
 * 返回此列表中的元素的列表迭代器(按适当顺序)。
 * <p>The returned list iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
 * @see #listIterator(int)
 */
public ListIterator<E> listIterator() {
    return new ListItr(0);
}

        两种重载形式都是调用列表迭代器有参构造方法。

/**
 * An optimized version of AbstractList.ListItr  优化版本
 */
private class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) { //有参构造方法,指定下一个返回元素的下标
        super();
        cursor = index;
    }
    //是否还有前一位元素
    public boolean hasPrevious() {
        return cursor != 0;
    }
    //获取下一个返回元素的下标
    public int nextIndex() {
        return cursor;
    }
    //获取前一位返回元素的下标
    public int previousIndex() {
        return cursor - 1;
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        checkForComodification();
        int i = cursor - 1;
        if (i < 0)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i;
        return (E) elementData[lastRet = i];
    }//这里要注意理解 cursor 与 lastRet 的含义

    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.set(lastRet, e);//调用数组列表的修改方法
        } catch (IndexOutOfBoundsException ex) {//注意捕获与抛出的异常,之所以出现下标越界,是并发修改导致的
            throw new ConcurrentModificationException();
        }
    }//修改上一次返回的元素

    public void add(E e) {
        checkForComodification();

        try {
            int i = cursor;
            ArrayList.this.add(i, e);  //在下一个返回元素位置添加指定元素
            cursor = i + 1; //原来下一个返回元素已经右移一位,所以 cursor + 1
            lastRet = -1; //新添加的元素没有迭代返回
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

        ListItr  继承自 Itr ,主要新增了一些新的特性:

            # 支持从指定位置开始迭代返回元素

            # 新增了从右到左的迭代方式

            # 新增了添加和修改方法

        对于迭代器 “以正确的顺序返回元素” 这一说法,在数组列表中,就是底层数组中的顺序。

 

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!