下面是迭代器的实现:
public Iterator<E> iterator() {
return new Itr();
}复制代码
在这里它是创建了一个Itr类,它实现了Iterator接口,是AbstractList类的私有内部类。
这个类里面有三个变量:
1. cursor也就是游标变量
2. lastRet变量:它指的是在最近一次调用next方法或者previous方法的时候返回的元素的索引值。当调用remove方法的时候它会被重置为-1。
3. expectedModCount变量:用来记录期望修改的次数,是迭代器用来检测是否发生了并发的修改操作。
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();
}
}复制代码
上面是next方法的实现。从它的实现可以看得出,每调用一次该方法,游标的值就会加1,然后调用get方法获得下一个值,同时把i赋给lastRet。但是在它抽象类中是没有实现get方法,它留给具体的实现类来实现的,所以这是个模板方法。
checkForCommodification方法是用来处理ConcurrentModificationException异常的,当实际修改的次数与期望修改的次数不一样的时候,就说明该集合处于多线程的环境下。
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}复制代码
上面是迭代器的删除方法。可以看出一个规律是要在调用了next方法之后,才可调用remove方法,删除的元素的索引就是lastRet,而这里真正调用的是抽象类的remove方法,这个方法在抽象类的实现是要抛出不支持该操作的异常,这个删除方法也是要留给它的子类来实现的。
public boolean equals(Object o) {
if (o == this)
return true;
if (!(o instanceof List))
return false;
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator();
while (e1.hasNext() && e2.hasNext()) {
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext());
}
复制代码
上面是2个集合判等的方法。这个方法就利用了ListIterator来进行元素的遍历,判断每个元素是否相等,其中从if判断到最后一句return,这段代码是比较精妙的。首先我们的习惯思维是循环判断2个集合的每一个元素是否相等,往往会在if里面直接去掉!,但这样做实际上为时过早,因为要判定2个集合的元素都相等,需要经过2次的循环才能确定。第一次循环是为了分别得到2个集合里面的元素,第二次遍历是判断是否已经遍历结束,只有遍历结束了才可以返回true或false。而加了!就可以直接返回false了。
ListIterator<E> it = listIterator(fromIndex);
for (int i=0, n=toIndex-fromIndex; i<n; i++) {
it.next();
it.remove();
}复制代码
上面是删除某个区间的元素的方法,其中toIndex所指向的元素不被包含进去。若干fromIndex==toIndex那么这个方法就不起作用。可以看出for循环和迭代器都用到了。同时这里也进行了一个小的优化是:计算出要遍历的次数n,儿不是直接用i<toIndex-fromIndex,因为这样会增加循环的时候的运算次数。
最后需要强调的一点是:凡是用到迭代器来进行修改操作,在多线程的环境下回导致同步修改的异常。
下一篇我们会介绍ArrayList的主要部分。由于个人的认知水平有限,如有不正确的地方,欢迎大家指正。