迭代器用于遍历集合对象
迭代器模式(Iterator Design Pattern)将集合对象的遍历操作从集合类中拆分出来,放到迭代器类中,让两者的职责更加单一。
一个完整的迭代器模式一般会涉及容器和容器迭代器两部分内容。
Iterator 接口
// 接口定义方式一
public interface Iterator<E> {
boolean hasNext();
void next();
E currentItem();
}
// 接口定义方式二
public interface Iterator<E> {
boolean hasNext();
E next();
}
- 第一种定义中,
next()
函数用来将游标后移一位元素,currentItem()
函数用来返回当前游标指向的元素。 - 在第二种定义中,返回当前元素与后移一位这两个操作,要放到同一个函数
next()
中完成。
第一种定义方式更加灵活一些,比如我们可以多次调用 currentItem()
查询当前元素,而不移动游标。
public class ArrayIterator<E> implements Iterator<E> {
private int cursor;
private ArrayList<E> arrayList;
public ArrayIterator(ArrayList<E> arrayList) {
this.cursor = 0;
this.arrayList = arrayList;
}
@Override
public boolean hasNext() {
return cursor != arrayList.size(); //注意这里,cursor在指向最后一个元素的时候,hasNext()仍旧返回true。
}
@Override
public void next() {
cursor++;
}
@Override
public E currentItem() {
if (cursor >= arrayList.size()) {
throw new NoSuchElementException();
}
return arrayList.get(cursor);
}
}
public interface List<E> {
Iterator iterator();
}
public class ArrayList<E> implements List<E> {
private int cursor;
private ArrayList<E> arrayList;
public Iterator iterator() {
return new ArrayIterator(this);
}
}
public class Demo {
public static void main(String[] args) {
List<String> names = new ArrayList<>();
names.add("xzg");
names.add("wang");
names.add("zheng");
Iterator<String> iterator = names.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.currentItem());
iterator.next();
}
}
}
- 迭代器中需要定义
hasNext()
、currentItem()
、next()
三个最基本的方法。 - 待遍历的容器对象通过依赖注入传递到迭代器类中。
- 容器通过 iterator() 方法来创建迭代器。
for循环与迭代器循环各有优势
-
for
循环遍历方式比起迭代器遍历方式,代码看起来更加简洁。对于类似数组和链表这样的数据结构,遍历方式比较简单,直接使用 for 循环来遍历就足够了
-
复杂的数据结构,有复杂的遍历方式,迭代器更适合。
应对复杂性的方法就是拆分。我们可以将遍历操作拆分到迭代器类中。比如,针对图的遍历,我们就可以定义
DFSIterator
、BFSIterator
两个迭代器类,让它们分别来实现深度优先遍历
和广度优先遍历
。 -
多个迭代器进行不同的遍历而互不影响
将游标指向的当前位置等信息,存储在迭代器类中,每个迭代器独享游标信息。这样,就可以创建多个不同的迭代器,同时对同一个容器进行遍历而互不影响。
-
容器和迭代器都提供了抽象的接口,基于接口而非具体的实现编程
当需要切换新的遍历算法的时候,比如,从前往后遍历链表切换成从后往前遍历链表,客户端代码只需要将迭代器类从
LinkedIterator
切换为ReversedLinkedIterator
即可,其他代码都不需要修改。
除此之外,添加新的遍历算法,我们只需要扩展新的迭代器类,也更符合开闭原则。