ArrayList-1-迭代器内部类

内部类

  ArrayList有四个内部类,如下所示:

private class Itr implements Iterator<E>
private class ListItr extends Itr implements ListIterator<E>
private class SubList extends AbstractList<Eimplements RandomAccess 
static final class ArrayListSpliterator<Eimplements Spliterator<E

  本文主要分析ArrayList的迭代器内部类的方法。

        其中Itr类实现了Iterator接口,重写了hasNext()方法、next()方法和remove()方法等方法。

  ListItr类继承了Itr类,且实现了ListIterator接口,重写了 hasPrevious()方法, nextIndex()方法,previousIndex()方法, previous()方法,set()方法和add()方法。

内部类Itr

  关于内部类Itr,该类实现了Iterator接口,而且是一个私有内部类。我们可以通过ArrayList的iterator()方法获取。

成员变量

// cursor指向下一个元素
int cursor;       // index of next element to return
// 返回最后一个元素的索引,假如没有了返回-1。其实他表示的就是当前元素的位置
int lastRet = -1// index of last element returned; -1 if no such
// 存储集合修改次数的变量,为了判断是否有多个线程访问修改
int expectedModCount = modCount;

构造方法

  默认空参构造。

成员方法

① hasNext方法

/**
 * 判断是否还有下一个元素
 * 原理是判断下一个元素的索引是否等于集合的大小
 */

public boolean hasNext() {
    return cursor != size;
}

注意:hasNext方法不会导致游标cursor移动。

② next方法

/**
 * 返回当前迭代的元素
 */

public E next() {
    // 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
    checkForComodification();
    int i = cursor;
    // 当cursor指向的索引大于集合的大小,就抛出没有元素异常,表示没有元素可以迭代
    if (i >= size)
        throw new NoSuchElementException();
    // 把ArrayList.this.elementData的数据赋给局部对象数组elementData
    Object[] elementData = ArrayList.this.elementData;
    // 当cursor指向的索引大于集合缓存数组的长度时,抛出并发修改异常
    // 原因是修改了集合。
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    // cursor指向的位置后移
    cursor = i + 1;
    // lastRet加一,然后返回当前元素的值
    return (E) elementData[lastRet = i];
}

  关于第一个判断i >= size

  i等于cursor,也就是需要返回的元素的下标,因为索引是从0开始,所以i,也就是cursor是不可能大于等于size的。

  关于第二个判断i >= elementData.length

  这个判断原因是集合数组被修改过,因为在经过第一个判断之后,i是一定小于size的,按道理是i<size<=element.length的,所以i是不可能大于等于elementData.length的,除非你将集合数组的大小改变了,例如调用了trimToSize()方法。

③ remove方法

/**
 * 移除当前元素
 */

public void remove() {
    // 当lastRet小于0,抛出异常
    if (lastRet < 0)
        throw new IllegalStateException();
    // 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
    checkForComodification();

    try {
        // 调用ArrayList的remove方法移除当前索引的元素
        ArrayList.this.remove(lastRet);
        // 将当前索引赋值给cursor变量
        cursor = lastRet;
        // 使lastRet指针重置为-1
        lastRet = -1;
        // 调用ArrayList的remove方法会让modCount加一
        // 重将新的修改次数赋值给expectedModCount变量
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

  关于lastRet = -1;这句,因为 lastRet 代表的那个元素已经被删除了,直接赋值为-1。

  假如我们在单线程使用增强for循环遍历的时候使用ArrayList的remove方法删除元素,会抛出并发修改异常,假如我们使用迭代器的remove方法是不会出现这个问题的,因为在该迭代器的remove方法中更新了expectedModCount的值。expectedModCount = modCount;

注意:Itr的remove方法必须在next方法之后使用,且在next方法后只能使用一次。因为在一次remove时将lastRet已经置为-1了,再次进入remove时会判断 lastRet是否小于0。

内部类ListItr

该私有内部类,继承上面的私有内部类Itr,实现了ListIterator接口

private class ListItr extends Itr implements ListIterator<E>

成员变量

继承父类Itr的,如下:

// cursor指向下一个元素
int cursor;       // index of next element to return
// 返回最后一个元素的索引,假如没有了返回-1。其实他表示的就是当前元素
int lastRet = -1// index of last element returned; -1 if no such
// 存储集合修改次数的变量,为了判断是否有多个线程访问修改
int expectedModCount = modCount;

构造方法

/**
 * 参数一般为数组的长度
 */

ListItr(int index) {
    super();
    cursor = index;
}

子类自己的成员方法

  相对于Itr迭代器,这个迭代器除了有remove方法之外,还有add、set等方法。在一些方法中在调用ArrayList方法操作之后,都会重新设置expectedModCount的值,就不会在单线程的迭代过程中发生并发修改异常了。

① hasPrevious方法

/**
 * 如果以逆向遍历列表,列表迭代器有多个元素,则返回 true。
 */

public boolean hasPrevious() {
    return cursor != 0;
}

② nextIndex方法

/**
 * 对next的后续调用所返回元素的索引
 * 如果列表迭代器在列表的结尾,则返回列表大小。
 */

public int nextIndex() {
    return cursor;
}

③  previousIndex方法

/**
 * 对previous的后续调用所返回元素的索引
 * 如果列表迭代器在列表的开始,则返回 -1。
 */

public int previousIndex() {
    return cursor - 1;
}

④ previous方法(反向遍历)

/**
 * 返回列表中的前一个元素。反向遍历
 * 可以重复调用此方法来迭代列表,或混合调用 next 来前后移动
 * (注意交替调用 next 和 previous 将重复返回相同的元素)
 */

public E previous() {
    // 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
    checkForComodification();
    // 定义i为上个元素的下标
    int i = cursor - 1;
    // i小于0抛出异常
    if (i < 0)
        throw new NoSuchElementException();
    // 将集合的缓冲数组赋值给变量elementData
    Object[] elementData = ArrayList.this.elementData;
    // 假如i大于缓冲数组的长度则抛出并发修改异常,原因是集合数组大小可能被修改了
    if (i >= elementData.length)
        throw new ConcurrentModificationException();
    // cursor变量减一
    cursor = i;
    // 将i赋值给lastRet,相当于lastRet为当前元素的索引,并返回上个元素的值
    return (E) elementData[lastRet = i];
}

⑤ set方法

/**
 * 为当前下标重新赋值
 */

public void set(E e) {
    // 当astRet小于0,抛出异常
    if (lastRet < 0)
        throw new IllegalStateException();
    // 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
    checkForComodification();

    try {
        // 调用ArrayList集合的set方法,将元素设置到lastRet索引的位置
        ArrayList.this.set(lastRet, e);
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}

⑥ add方法

public void add(E e) {
    // 检查当前Itr修改次数和ArrayList是否一致,不一致则抛出并发修改异常
    checkForComodification();

    try {
        int i = cursor;
        // 调用ArrayList集合的add方法,将元素设置到下一个索引的位置
        // 操作会使modCount的值加一
        ArrayList.this.add(i, e);
        cursor = i + 1;
        // 重置lastRet
        lastRet = -1;
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}


原文始发于微信公众号(肝帝笔记):ArrayList-1-迭代器内部类

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/31532.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!