Java 容器中的迭代器模式
容器
J a v a Java Java 中提供了两大类容器 C o l l e c t i o n Collection Collection 和 M a p Map Map,大致框架图如下:
![容器](https://i-blog.csdnimg.cn/blog_migrate/a7d6b2abefdb8e5467c94f7700b8be2f.png)
迭代器模式
容器为我们提供了多种多样的保存对象的方式,对于容器的遍历,可能也会有多种方式,比如对于链表的遍历,可能希望容器类既可以提供从前往后的遍历,又可以提供从后往前的遍历,迭代器模式就是将集合对象的遍历操作从集合里面抽离出来,放到迭代器类里面,让两者职责更加单一。
一个基本的迭代器接口如下所示:
public interface MyIterator<E> {
boolean hasNext();
void next();
E currentItem();
}
当要使用迭代器时,可以通过组合的方式让迭代器类获得容器对象,也可以让迭代器类成为容器类的内部类。下面的实现采用内部类的方式,实现了一个 M y A r r a y L i s t MyArrayList MyArrayList,只是为了演示迭代器模式,因此内部采用$ Java$ 内置的 A r r a y L i s t ArrayList ArrayList。
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());
}
会发生不可预期的结果,就是当我们删除不同的元素时,最后的输出和我们调用 n e x t ( ) next() next() 的预期输出是不一样的,并且会给编程人员造成比较大的困扰。因此在 J a v a Java Java 普通容器的内部是不允许在迭代过程中,对容器元素进行增删的,当在遍历过程中,进行增删的话,会直接报异常。那么是如何实现的呢?
通过记录一个 $modCount $值,在容器内部会记录 m o d C o u n t modCount modCount 值,每当对容器进行增删的时候, m o d C o u n t modCount modCount都会改变,在创建迭代器的时候,会将此 m o d C o u n t modCount modCount 值赋值给迭代器内部的 e x p e c t e d M o d C o u n t expectedModCount expectedModCount,在每次 n e x t ( ) next() next() 时进行判断,如果不相等,就直接抛出异常, J a v a Java Java 内部 A r r a y L i s t ArrayList ArrayList 实现如下:
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();
}
那么如何实现快照形式的迭代呢?使用快照的方式, C o p y O n W r i t e A r r a y L i s t CopyOnWriteArrayList CopyOnWriteArrayList 内部就是使用此方式。
// 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;
}