Java 容器中的迭代器模式
容器
中提供了两大类容器 和 ,大致框架图如下:
迭代器模式
容器为我们提供了多种多样的保存对象的方式,对于容器的遍历,可能也会有多种方式,比如对于链表的遍历,可能希望容器类既可以提供从前往后的遍历,又可以提供从后往前的遍历,迭代器模式就是将集合对象的遍历操作从集合里面抽离出来,放到迭代器类里面,让两者职责更加单一。
一个基本的迭代器接口如下所示:
public interface MyIterator<E> {
boolean hasNext();
void next();
E currentItem();
}
当要使用迭代器时,可以通过组合的方式让迭代器类获得容器对象,也可以让迭代器类成为容器类的内部类。下面的实现采用内部类的方式,实现了一个,只是为了演示迭代器模式,因此内部采用 内置的。
import java.util.ArrayList;
import java.util.Iterator;
public class MyArrayList<E> {
ArrayList<E> arrayList;
public MyArrayList() {
this.arrayList = new ArrayList<E>();
}
// 得到具体的迭代器。
public MyIterator<E> iterator() {
return new MyArrayListIterator();
}
public void add(E e) {
this.arrayList.add(e);
}
public void remove(int index) {
this.arrayList.remove(index);
}
private class MyArrayListIterator implements MyIterator<E> {
int currentIndex = 0;
@Override
public boolean hasNext() {
return currentIndex < MyArrayList.this.arrayList.size() ;
}
@Override
public void next() {
currentIndex++;
}
@Override
public E currentItem() {
return MyArrayList.this.arrayList.get(currentIndex);
}
}
// 具体的测试
public static void main(String[] args) {
MyArrayList<String> myArrayList = new MyArrayList<>();
myArrayList.add("a");
myArrayList.add("b");
myArrayList.add("c");
MyIterator<String> iterator = myArrayList.iterator();
while (iterator.hasNext()){
System.out.println(iterator.currentItem());
iterator.next();
}
}
}
那么如果我们在容器的迭代过程中,删除了容器中的元素会发生什么呢?再来进行具体的测试:
public static void main(String[] args) {
MyArrayList<String> myArrayList = new MyArrayList<>();
myArrayList.add("a");
myArrayList.add("b");
myArrayList.add("c");
myArrayList.add("d");
MyIterator<String> iterator = myArrayList.iterator();
iterator.next();
iterator.next();
// myArrayList.remove(3);
myArrayList.remove(1);
System.out.println(iterator.currentItem());
}
会发生不可预期的结果,就是当我们删除不同的元素时,最后的输出和我们调用 的预期输出是不一样的,并且会给编程人员造成比较大的困扰。因此在 普通容器的内部是不允许在迭代过程中,对容器元素进行增删的,当在遍历过程中,进行增删的话,会直接报异常。那么是如何实现的呢?
通过记录一个 值,在容器内部会记录 值,每当对容器进行增删的时候,都会改变,在创建迭代器的时候,会将此 值赋值给迭代器内部的 ,在每次 时进行判断,如果不相等,就直接抛出异常, 内部 实现如下:
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
那么如何实现快照形式的迭代呢?使用快照的方式, 内部就是使用此方式。
// CopyOnWrite 内部使用的 snapshot
/** Snapshot of the array */
private final Object[] snapshot;
/** Index of element to be returned by subsequent call to next. */
private int cursor;
private COWIterator(Object[] elements, int initialCursor) {
cursor = initialCursor;
snapshot = elements;
}