AbstaractList类中的listIterator()的实现分析

Iterator接口

  Iterator提供了一种机制来遍历元素。其中包含了以下4种方法:

  • hasNext()
  • next()
  • remove()
  • forEachRemaining(Consumer<? super E>)\

最后一种方法用来对集合中的每个元素执行对应的动作,只需要添加相应的lambda表达式即可。其他方法的语义可直接参考JDK源代码即可。

ListIterator接口

  该接口在Iterator接口基础之上扩展了以下主要方法:

  • hasPrevious()
  • previous()
  • add()
  • set(E e)

这里面通过前两个方法实现了前向迭代有序对象,其他的方法会通过迭代器对被迭代对象的内容做修改。在java.util中的集合标准实现中,实现了ListIterator接口的类会提供一个listIterator()方法返回对应的迭代器对象。这里分析AbstractList类中的实现。
  在类AbstractList中,通过内部类Itr类(Iterator的一个实现类),来完成基本的3个迭代方法。以下只提JDK中的next()实现:

private class Itr implements Iterator<E> { /**
   * Index of element to be returned by subsequent call to next.
     */
    int cursor = 0;

    /**
     * Index of element returned by most recent call to next or
     * previous.  Reset to -1 if this element is deleted by a call
     * to remove.
     */
    int lastRet = -1;
	...
	public E next() {
	    checkForComodification();
	    try {
	        int i = cursor;
	        E next = get(i);
	        lastRet = i;
	        cursor = i + 1;
	        return next;
	    } catch (IndexOutOfBoundsException e) {
	        checkForComodification();
	        throw new NoSuchElementException();
	    }
	}
	...
}

这里的lastRetcursor的关系通过语义也就能清楚。lastRetcursor所指向的元素构成了前后关系。对于listIterator()的实现,通过内部类ListItr扩展Itr,且实现ListIterator接口,对增添的方法做了实现,截取部分源代码:

private class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) {
        cursor = index;
    }

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

    public E previous() {
        checkForComodification();
        try {
            int i = cursor - 1;
            E previous = get(i);
            lastRet = cursor = i;
            return previous;
        } catch (IndexOutOfBoundsException e) {
            checkForComodification();
            throw new NoSuchElementException();
        }
    }

    public int nextIndex() {
        return cursor;
    }

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

    public void set(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();
        try {
            AbstractList.this.set(lastRet, e);
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    public void add(E e) {
        checkForComodification();
        try {
            int i = cursor;
            AbstractList.this.add(i, e);
            lastRet = -1;
            cursor = i + 1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

previous()实现中关键的一句是:

lastRet = cursor = i;

这与next()中的实现逻辑是有区别的:

lastRet = i;
cursor = i + 1;

其原因个人理解如下:previous的使用场景一般是对迭代器做了next()操作之后的,那么这个向下文也说明了lastRetcursor的值小1。改变了迭代方向,lastRetcursor值一样,如果再调用next(),会保持和之前的状态(lastRet+1=cursor)。
  set(E e)实现中的关键一句为:

 AbstractList.this.set(lastRet, e);

说明,无论当前的迭代器在执行next()操作还是previous()操作,该方法修改的都是刚才访问过的那个元素。从这个角度再看previous()的实现,更好理解一些。
  add()方法中需要注意的一点是

AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;

即在cursor所指的位置添加数据,同时对cursor的更新也以为着迭代器不会访问刚才添加的数据。让lastRet失效,这也就意味着,不能在调用add()操作之后紧接着就调用set(E e),所以在set(E e)实现体中包含了下面的代码:

 if (lastRet < 0)
	throw new IllegalStateException();

这种类似的操作在remove()中也存在(实现在Itr类中)。
  最后,为了防止对同一个集合对象同时使用多个迭代器进行操作,造成相互干扰,实现代码通过记录集合修改次数modCount,检查是否与对应迭代器中的expectedModCount一致。modCount的修改逻辑存在于AbstractList子类的add(E e)set(E e)逻辑中。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值