(注意:本文基于JDK1.8)
ArrayList中重写了基类AbstractList的两个listIterator方法,这两个方法的返回值都是ListItr对象,ListItr类是作为增强型的迭代器而设计的,接下来就一探究竟!
0、ListItr的类结构
private class ListItr extends Itr implements ListIterator {
…………省略很多代码…………
}
ListItr类定义在ArrayList的内部,作为ArrayList的普通内部类,ListItr扩展了基础的顺序迭代器类Itr(详细介绍),并实现了ListIterator接口(见本文1号知识点)
1、ListIterator接口介绍
public interface ListIterator extends Iterator {
…………省略很多代码…………
}
ListIterator接口定义了作为线性表迭代器的功能(能力),ListIterator扩展了普通迭代器Iterator接口的功能(具备的能力),看下ListIterator定义了哪些方法
根据上文截图,在Iterator接口的基础功能之上,又新增了add、hasPrevious、nextIndex、previous、previousIndex、set方法
2、ListItr对象的创建
ListItr(int index) {
super();
cursor = index;
}
ListItr类接受一个参数的构造方法,传入的index值代表线性表的下标值,在构造方法的内部首先调用了父类的构造方法(见本文3号知识点),接着将传入的参数index赋值给ListItr对象持有的实例变量cursor保存起来,注意实例变量cursor是定义在父类Itr中的,这就是ListItr对象的创建,当然实例变量也执行了初始化
3、父类Itr的构造方法
Itr() {}
父类Itr的构造方法没有做任何初始化的工作(注意:Itr类中定义的实例变量,在Itr类加载到JVM时已经完成了初始化)
4、ListItr中实现的hasPrevious方法
public boolean hasPrevious() {
return cursor != 0;
}
hasPrevious方法用于判断当前线性表遍历过程中是否存在上一个元素,当ListItr对象持有的实例变量cursor不等于0时,我们则认为当前ArrayList中仍然存在上一个元素没有遍历过,当cursor等于0时,已经是最左侧的元素,或者说是第一个元素,当然不会有上一个元素的存在
5、ListItr中实现的nextIndex方法
public int nextIndex() {
return cursor;
}
nextIndex方法返回当前迭代器遍历的当前元素下标cursor,在迭代器中,cursor的初始值是0,第一次调用Itr中的next方法时,返回的正是cursor值为0的元素
6、ListItr中实现的previousIndex方法
public int previousIndex() {
return cursor - 1;
}
previousIndex方法返回迭代器即将遍历元素的上一个元素,在实现上直接使用cursor-1即可得到上一个元素,因为cursor值代表的是迭代器调用next返回的元素下标
7、ListItr中实现的previousIndex方法
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];
}
previous方法是ListItr迭代器对象用来返回上一个元素对象的方法
a、首先调用了checkForComodification方法(见本文8号知识点)检查ArrayList持有的元素数量是否发生过改变
b、检查通过后,先获取ListItr对象持有的cursor值,接着将cursor值减去1,临时交给局部变量i保存,如果i的值小于0,说明已经没有上一个元素,此时会抛出一个NoSuchElementExcepton对象(使用本文中4号知识点提及的方法即可避免这种情况)
c、接着获取当前ArrayList对象持有的数组对象elementData并赋值给局部变量elementData保存,然后再次对即将遍历的局部变量i与数组对象elementData的长度进行对比,如果即将遍历的元素下标i大于等于数组elementData的长度,说明在遍历过程中,ArrayList变更了元素数量,此时就会抛出ConcurrentModificationException
d、检查再次通过后,则先更新当前遍历元素的下标cursor为局部变量i的值,在方法的最后则是执行return语句,先把返回的元素下标记录在ListItr对象持有的实例变量lastRet中(lastRet定义在父类Itr中),接着从数组对象elementData中的下标lastRet中将元素对象返回调用者
8、定义在父类Itr中的checkForComodification方法
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
checkForComodification方法的功能是fail-fast机制的检查者,它检查当前线性表在遍历过程中,是否发生过变更元素的行为(添加、删除、清除),每次元素改变,ArrayList对象持有的modCount都会+1的操作(注意:modCount定义在ArrayList的父类AbstractList中),而expectedModCount则是ListItr对象持有的实例遍历,它在ListItr对象创建时,存储了ArrayList最初的modCount值,通过对比ArrayList对象持有的modCount与ListItr对象持有的expectedModCount值,如果不相等,则会直接抛出ConcurrentModificationException对象!
9、ListItr中实现的set方法
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
set方法可以改变最后一次通过next或者previous方法返回的元素对象,或者说它可以变更lastRet下标处的元素对象
首先做了两个检查、一个是检查lastRet下标值不能小于0,否则就抛出IllegalStateException对象,另一个则是通过checkForComodification方法(见本文8号知识点)的检查,在try ..catch...代码块中,则是通过ArrayList对象的set方法进行元素的变更,set方法接受lastRet、以及传入的元素对象e,set方法可能会抛出IndexOutOfBoundsException,catch代码块中会捕获该异常,然后再次抛出一个ConcurrentModificationException对象(因为只有ArrayList中的元素数量改变了,才可能出现IndexOutOfBoundsException的问题)
10、ListItr实现的add方法
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
add方法可以添加一个元素到当前ArrayList对象中,它接受一个参数类型为E的对象
a、首先调用checkForComodification方法(见本文8号知识点)做一次检查
b、检查通过后,则是try...catch...代码块的执行,先将ListItr对象持有的cursor赋值给局部变量i,真正添加元素的工作是通过ArrayList的add方法完成的,add方法接受下标值i、以及元素对象e
c、元素添加完成后,将新增元素的数量+1,并赋值给ListItr持有的cursor,将ListItr记录的最后遍历返回的元素下标lastRet更新为初始值-1,说明迭代器上一次并没有进行遍历行为,而是其他行为如添加、删除、更新元素
d、ArrayList添加完元素后,ArrayList对象持有的modCount已经增加了1,此时就需要将ListItr对象持有expectedModCount重新赋值一次
e、ArrayList的add方法可能会抛出IndexOutBoundsException,此处做了捕获处理,捕获之后再次抛出ConcurrentModificationException对象,因为add方法抛出IndexOutOfBoundsException的根本原因是ArrayList的元素数量在某一刻改变了(进一步说明ArrayList无法在多线程环境下使用)
总结
a、ListItr比Itr迭代器新增加向前遍历元素的能力,这是通过hasPrevious、previousIndex、previous方法来实现的
b、ListItr也比Itr迭代器新增了添加元素的能力,add方法实现了该功能(见本文10号知识点)
c、ListItr比Itr新增了修改元素的能力,set方法实现了该功能(见本文9号知识点)