设计模式——迭代器模式
概述相信大家平时开发过程中没少用过遍历集合、数组这些语法。一般来讲,遍历集合数据有三种方法:for 循环、foreach 循环、iterator 迭代器。实际上,foreach 循环只是一个语法糖而已,底层是基于迭代器来实现的。foreach 循环代码的底层实现,就是第三种遍历方式(迭代器遍历)。这两种遍历方式可以看作同一种遍历方式,也就是迭代器遍历方式。迭代器模式:(Iterator Desig
概述
相信大家平时开发过程中没少用过遍历集合、数组这些语法。
一般来讲,遍历集合数据有三种方法:for 循环、foreach 循环、iterator 迭代器。
实际上,foreach 循环只是一个语法糖而已,底层是基于迭代器来实现的。foreach 循环代码的底层实现,就是第三种遍历方式(迭代器遍历)。这两种遍历方式可以看作同一种遍历方式,也就是迭代器遍历方式。
迭代器模式:(Iterator Design Pattern)也叫做游标模式(Cursor Design Pattern)。提供一种方法顺序访问一个聚合对象中的各种元素,而又不暴露该对象的内部表示。
何时使用:
- 访问一个聚合对象的内容而无须暴露它的内部表示。
- 需要为聚合对象提供多种遍历方式。
- 为遍历不同的聚合结构提供一个统一的接口。
UML 类图:
角色组成:
- 抽象容器(Aggregate):一般是一个接口,提供一个iterator()方法,例如java中的Collection接口,List接口,Set接口等。
- 具体容器(ConcreteAggregate):就是抽象容器的具体实现类,比如List接口的有序列表实现ArrayList,List接口的链表实现LinkList,Set接口的哈希列表的实现HashSet等。
- 抽象迭代器(Iterator):定义按顺序逐个遍历元素的接口,定义遍历元素所需要的方法取得下一个元素的方法next()和判断是否遍历结束的方法hasNext()。
通用代码
Iterator.java
public interface Iterator {
boolean hasNext();
Object next();
}
ConcreteIterator.java
public class ConcreteIterator implements Iterator {
private List list;
private int cursor = 0;
public ConcreteIterator(List list) {
this.list = list;
}
@Override
public boolean hasNext() {
if (cursor == list.size()) {
return false;
}
return true;
}
@Override
public Object next() {
Object obj = null;
if (this.hasNext()) {
obj = this.list.get(cursor++);
}
return obj;
}
}
Aggregate.java
public interface Aggregate {
void add(Object obj);
void remove(Object obj);
Iterator iterator();
}
ConcreteAggregate.java
public class ConcreteAggregate implements Aggregate {
private List list = new ArrayList();
@Override
public void add(Object obj) {
list.add(obj);
}
@Override
public void remove(Object obj) {
list.remove(obj);
}
@Override
public Iterator iterator() {
return new ConcreteIterator(list);
}
}
测试:
public class Test {
public static void main(String[] args) {
Aggregate aggregate = new ConcreteAggregate();
aggregate.add("小胖");
aggregate.add("小黑");
aggregate.add("小六");
Iterator iterator = aggregate.iterator();
while (iterator.hasNext()) {
String str = (String) iterator.next();
System.out.println(str);
}
}
}
结果:
小胖
小黑
小六
迭代器模式的优势
-
简化了遍历方式,对于对象集合的遍历,还是比较麻烦的,对于数组或者有序列表,我们尚可以通过游标来取得,但用户需要在对集合了解很清楚的前提下,自行遍历对象,但是对于hash表来说,用户遍历起来就比较麻烦了。而引入了迭代器方法后,用户用起来就简单的多了。
-
可以提供多种遍历方式,比如说对有序列表,我们可以根据需要提供正序遍历,倒序遍历两种迭代器,用户用起来只需要得到我们实现好的迭代器,就可以方便的对集合进行遍历了。
-
封装性良好,用户只需要得到迭代器就可以遍历,而对于遍历算法则不用去关心。
ArrayList 相关源码
接下来我们看一下 ArrayList 遍历相关的源码使用的迭代器模式:
首先是接口 Iterator.java
public interface Iterator<E> {
boolean hasNext();
E next();
// 省略其他代码
}
然后是类 Itr ,对应着 ConcreteIterator 角色,不过这个类是 ArrayList 的一个内部类。
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
public boolean hasNext() {
return cursor != size;
}
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();
}
// 省略其他代码
}
接下来是类 List ,对应着 Aggregate 角色。
public interface List<E> extends Collection<E> {
Iterator<E> iterator();
// 省略其他代码
}
最后是类 ArrayList , 对应着 ConcreteAggregate 角色。
public class ArrayList<E> extends AbstractList<E> implements List<E>
{
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
// 此处代码省略
}
// 省略其他代码
}
总结
有了 for 循环遍历,我们为什么还需要迭代器遍历呐?
对于类似数组和链表这样的数据结构,遍历方式比较简单,直接使用 for 循环来遍历就足够了。但是,对于复杂的数据结构(比如树、图)来说,有各种复杂的遍历方式。如果由客户端代码来实现这些遍历算法,势必增加开发成本,而且容易写错。
看这样一个例子:
public class Test {
public static void main(String[] args) {
List<Person> list = new ArrayList<>();
list.add(new Person("小黑", 25));
Iterator it = list.iterator();
while (it.hasNext()) {
Person p = (Person)it.next();
System.out.println(p.getName());
}
}
static class Person {
String name;
int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public int getAge() {
return age;
}
}
}
while (it.hasNext()) {
Person p = (Person)it.next();
System.out.println(p.getName());
}
这里使用了 Iterator 的 hasNex 方法和 next 方法,并没有调用 ArrayList 的方法。也就是说,这里的 while 循环并不依赖于 ArrayList 的实现。
假如 ArrayList 的开发人员决定切换新的遍历算法的时候,只需要修改迭代器类的代码即可,只要 ArrayList 的 iterator 方法能正确的返回 Iterator 的实例,即使不对上面的 while 循环做任何修改,代码都可以正常工作。
更多推荐
所有评论(0)