Itr
和ListItr
的简介AbstractList的有两个内部类实现了迭代器,一个内部类Itr
实现了Iterator
接口,一个内部类ListItr
实现了ListIterator
接口,后者其实就是前者的升级版罢了,多了一些额外操作。
下面先来看一下返回这两个内部类实例的方法的注释:
public Iterator
前面的重要注释摘抄如下(字面意思请自行谷歌翻译,中文只进行重要的解释):
This implementation returns a straightforward implementation of the iterator interface, relying on the backing list’s
size()
,get(int)
, andremove(int)
methods.
Note that the iterator returned by this method will throw anUnsupportedOperationException
in response to itsremove
method unless the list’sremove(int)
method is overridden.
This implementation can be made to throw runtime exceptions in the face of concurrent modification, as described in the specification for the (protected)#modCount
field.
iterator
接口的实现类。backing list我理解是该迭代器持有的外部类对象(list对象),因为Itr
是一个成员内部类,所以必然会持有外部类对象。而Itr
会调用到size()
, get(int)
, and remove(int)
方法,所以说依赖于它们。remove(int)
方法时,由于默认实现是直接抛出UnsupportedOperationException
异常(AbstractList
里面的实现),所以导致迭代器的remove
也会抛异常。#modCount
实现,这是外部类对象(list对象)的成员,别的线程可能同时也会进行修改动作。public ListIterator
的注释如下:
This implementation returns a straightforward implementation of the
ListIterator
interface that extends the implementation of theIterator
interface returned by theiterator()
method. TheListIterator
implementation relies on the backing list’sget(int)
,set(int, E)
,add(int, E)
andremove(int)
methods.
Note that the list iterator returned by this implementation will throw anUnsupportedOperationException
in response to itsremove
,set
andadd
methods unless the list’sremove(int)
,set(int, E)
, andadd(int, E)
methods are overridden.
This implementation can be made to throw runtime exceptions in the face of concurrent modification, as described in the specification for the (protected)#modCount
field.
set(int, E), add(int, E)
方法。next()
,它还可以往回走previous()
。Itr
和ListItr
的具体实现这就上源码分析吧,具体细节看注释:
private class Itr implements Iterator<E> {
/**
* 下一次调用next将返回的元素索引
*/
int cursor = 0;
/**
* 最近一次调用next或者previous返回的元素的索引。
* 会被置为-1,当remove的调用后删除了lastRet代表的元素。
*/
int lastRet = -1;
/**
* expectedModCount字段是迭代器认为外部类list对象应该具有的值。
* 这个字段在迭代器初始化时就得到了。如果迭代器使用过程中,检测到
* 这个值和外部类对象的值不一样,则发生并发的修改。
*/
int expectedModCount = modCount;
public boolean hasNext() {
//会间接调用到外部类对象的size方法,没有默认的抛异常的实现,外部类list必须实现
return cursor != size();
}
public E next() {
checkForComodification();
try {
int i = cursor;//既然cursor是下一次next的返回索引,那就先取出来给i
E next = get(i);//调用到外部类AbstractList的get方法
lastRet = i;//既然已经用get函数访问了i索引,所以lastRet置为i
cursor = i + 1;//因为方向是next,所以cursor = cursor + 1
return next;
} catch (IndexOutOfBoundsException e) {
//get函数可能抛出IndexOutOfBoundsException,
//可能是因为外部类对象的modCount已经被改变,进而抛出ConcurrentModificationException
//但这也可能是因为迭代器已经到达末尾,所以checkForComodification()不会抛出异常
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet < 0)//说明上一次执行了迭代器的删除后,还没有进行过迭代器的移动,这种情况不允许删除
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);//调用外部类方法remove
if (lastRet < cursor)
cursor--;
lastRet = -1;//删除后,lastRet上一次访问的元素已经不存在,因为已经被删除了
//若不置-1,那么lastRet指向的是删除前lastRet+1那个元素,总之是不对的
expectedModCount = modCount;//删除后获取新的modCount
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ListItr
的previous()
方法会使得lastRet和cursor相同)。next()
的逻辑和后面介绍ListItr
的previous()
的逻辑,可以看出,在移动迭代器时,是按照cursor的位置来移动的。next()
会使得cursor加1,previous()
会使得cursor减1。next()
或者previous()
返回的元素的索引。所以Itr
初始化的时候lastRet的初值为-1,因为刚初始化的迭代器还没有返回过任何元素,所以置为-1,代表一个不可能的索引。remove()
执行后,会把lastRet置为-1,这很合理,因为lastRet索引元素已经被删除。remove()
执行后,迭代器的位置的往回走了一步(其实就是cursor减1了)。如果cursor不减1,那么迭代器将会在一个错误的位置上。remove()
的逻辑里面,有一句if (lastRet < cursor)
判断成功后才会cursor--
,这是因为后面介绍的ListItr
的previous()
方法会使得lastRet和cursor相同。再看ListItr
的源码吧:
private class ListItr extends Itr implements ListIterator<E> {
ListItr(int index) {
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;//只要cursor不等于0,就可以往回走
}
public E previous() {
checkForComodification();
try {
int i = cursor - 1;//往回走,所以cursor-1给局部变量
E previous = get(i);
lastRet = cursor = i;//因为get的参数是i,所以lastRet肯定也应该为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)//说明执行了remove或add后,还没有进行过next或previous操作
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.set(lastRet, e);
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
//add不用关心lastRet,即不在乎之前有没有remove或add
checkForComodification();
try {
int i = cursor;
AbstractList.this.add(i, e);
lastRet = -1;
cursor = i + 1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
hasPrevious()
和previous()
方法,previous()
方法会让cursor减1,然后让lastRet和cursor相同。previous()
方法会让lastRet和cursor相同,所以在remove()
的逻辑里才会去判断if (lastRet < cursor)
。如果此时lastRet < cursor
成立,说明刚执行了next操作;如果此时lastRet < cursor
不成立(即lastRet = cursor
),说明刚执行了previous操作。add(E e)
方法会往cursor索引位置添加元素,添加之前cursor索引元素以及后续元素会往右移动一步,此方法执行后会使得lastRet为-1,但此方法并不关心lastRet是否为-1,即不在乎是不是刚执行了remove()
或add()
方法。从下图可以看出(绿色节点为添加元素),无论连续执行多少次add(E e)
方法,cursor实际指向元素没有发生变化。下图第一个状态没有标注出lastRet的位置,因为此方法不关心lastRet。set(E e)
方法在执行前,必须判断出lastRet不为-1。lastRet为-1,说明刚执行过remove或add操作。此方法不会改变lastRet的值,因为此方法只是在替换元素。next()
或previous()
返回的那个元素的索引,所以next()
或previous()
会对lastRet作出妥当的处理。remove()
方法中要把lastRet置为-1,是因为执行完remove()
方法后原来lastRet指向的元素已经被删除掉了,所以lastRet需要指向一个不可能的元素。之所以remove()
方法中最开始要判断lastRet是否为-1,是因为执行了remove()
后不可以马上再执行remove()
,因为原来lastRet指向的元素已经被删除,马上再删也不知道该删哪个元素了。next()/previous()
后,再执行完remove()
后lastRet指向的元素就会失效了,所以需要lastRet置为-1。add()
方法中要把lastRet置为-1,是因为如果执行顺序是previous(); add();
,且假如add()
方法中没有把lastRet置为-1,那么此时lastRet指向的元素是错误的。如下图所示,lastRet如果不动弹的话,那么将会指向新增加元素(绿色节点),而这样肯定不符合lastRet的含义了。set(E e)
方法的执行的前提就是,lastRet处于有效状态,即lastRet指向的元素是有效的,不是-1。next()/previous()
方法能够使得lastRet恢复有效状态,而remove()/add()
会使得lastRet变成无效状态,remove()/set()
的执行前提是lastRet处于有效状态。