套路要深…
故事背景
今天要介绍一下迭代器,首先简单说明一下,什么是迭代器,为什么要使用迭代器。
迭代器(Iterate) 的意思就是反复做某件事情。
那为什么要反复做某件事情呢,比如我们有个容器里面装了很好东西(这些东西都是同一类型的),要从容器中取每一个东西出来,就要反复去做一个取出的事情。
故事主角
迭代器模式 : 提供一种方法顺序访问一个聚合对象中的各个元素,而又不暴露其内部的表示。
在迭代器模式结构中包含聚合和迭代器两个层次结构,并在迭代器中使用到了工厂方法模式。
在迭代器结构图中有如下几个角色:
- Iterator (抽象迭代器): 定义了访问和遍历元素的接口。
- ConcreteIterator(具体的迭代器):实现了抽象迭代器接口,完成对聚合对象的遍历操作。
- Aggregate(抽象的聚合类):存储和管理元素对象,声明一个创建迭代器的方法。充当抽象迭代器工厂的角色。
- ConcreteAggregate(具体的聚合类):实现了抽象聚会类申明的创建迭代器方法。
武功修练
通过上面对迭代器的了解和认识我们看一下Java集合框架中迭代器的使用。主要看一下ArrayList、LinkedList、HashSet。这三个是集合中的三个常用类,但是它们的实现机制是不一样的。这里对实现机制就不进行讲解,不懂的朋友可以自行查找资料。
首先看一下这三个类的结构图:
其中都是实现了 Iterable接口。下面我们再看一下这个Iterable
接口
// 这个就是Aggregate(抽象的聚合类)
public interface Iterable<T> {
// 申明 创建迭代器的方法
Iterator<T> iterator();
// 其他默认实现
default void forEach(Consumer<? super T> var1) {
Objects.requireNonNull(var1);
Iterator var2 = this.iterator();
while(var2.hasNext()) {
Object var3 = var2.next();
var1.accept(var3);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(this.iterator(), 0);
}
}
在来看一下Iterator
接口
// Iterator (抽象迭代器)
public interface Iterator<E> {
boolean hasNext(); // 是否有下一个元素
E next();//下一个元素
// 其他默认实现
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> var1) {
Objects.requireNonNull(var1);
while(this.hasNext()) {
var1.accept(this.next());
}
}
}
从上面的;类图和源码中,可以看到ArrayList、LinkedList、HashSet,是ConcreteAggregate(具体的聚合类),具体的聚合类中实现创建迭代器以及实现具体的迭代器!
如下是ArrayList
源码:
// 在ArrayList创建了一个迭代器
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
* jdk1.8版本
*/
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;
}
@SuppressWarnings("unchecked")
// 获取一个元素
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];
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
// 其他方法
}
如下是LinkedList
实现迭代器的部分代码:
public Iterator<E> descendingIterator() {
return new LinkedList.DescendingIterator();
}
private class DescendingIterator implements Iterator<E> {
private final LinkedList<E>.ListItr itr;
private DescendingIterator() {
this.itr = LinkedList.this.new ListItr(LinkedList.this.size());
}
public boolean hasNext() {
return this.itr.hasPrevious();
}
public E next() {
return this.itr.previous();
}
public void remove() {
this.itr.remove();
}
}
hashSet的源码实现就不进行说明了,感兴趣的朋友可以自行查看。
通过上面简单的类图和源码分析,发现了这几个类使用了迭代器模式。
举个简单的小例子说明一下迭代器的好处:
class IteratorDemo {
public static void process(Collection c) {
Iterator i = c.iterator(); //创建迭代器对象
//通过迭代器遍历聚合对象
while(i.hasNext()) {
System.out.println(i.next().toString());
}
}
public static void main(String args[]) {
Collection persons;
persons = new ArrayList(); //创建一个ArrayList类型的聚合对象
persons.add("2");
persons.add("5");
persons.add("3");
persons.add("7");
persons.add("0");
persons.add("4");
process(persons);
}
}
如果需要更换聚合类型,如将List改成Set即可,迭代遍历的代码无需进行修改。
总结
迭代器模式简化了聚合类对象的元素的遍历,封装了聚合类具体的遍历实现细节,能够实现聚合对象的内部数据和存储分离,使得聚合类的职责更加专一,同时提供统一的接口,方便客户端操作。
Next 期待下一篇吧!下一篇讲讲组合模式!
参考
- 史上最全设计模式导学
- 《Head First 设计模式》
- 《图解设计模式》
如果您觉得这篇博文对你有帮助,请点赞或者喜欢,让更多的人看到,谢谢!
如果帅气(美丽)、睿智(聪颖),和我一样简单善良的你看到本篇博文中存在问题,请指出,我虚心接受你让我成长的批评,谢谢阅读!
祝你今天开心愉快!
欢迎访问我的csdn博客,我们一同成长!
不管做什么,只要坚持下去就会看到不一样!在路上,不卑不亢!