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();
}
}
...
}
这里的lastRet
与cursor
的关系通过语义也就能清楚。lastRet
与cursor
所指向的元素构成了前后关系。对于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()
操作之后的,那么这个向下文也说明了lastRet
比cursor
的值小1。改变了迭代方向,lastRet
与cursor
值一样,如果再调用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)
逻辑中。