文章目录
- 一、介绍
- 二、迭代器模式中的角色
- 三、代码演示
-
- [1. 支持迭代的集合抽象接口(`BarIterable`)](#1. 支持迭代的集合抽象接口(
BarIterable
)) - [2. 具体的集合类(`BarList`)](#2. 具体的集合类(
BarList
)) - [3. 迭代器抽象接口(`FooIterator`)](#3. 迭代器抽象接口(
FooIterator
)) - [4. 迭代器具体实现类(`FooItr`)](#4. 迭代器具体实现类(
FooItr
)) - [5. 代码测试](#5. 代码测试)
- [1. 支持迭代的集合抽象接口(`BarIterable`)](#1. 支持迭代的集合抽象接口(
- 四、java中迭代器模式的应用
-
- [1. `Iterable`接口](#1.
Iterable
接口) - [2. 迭代器抽象接口`Iterator`](#2. 迭代器抽象接口
Iterator
) - [3. `ArrayList`集合对迭代器模式的实现](#3.
ArrayList
集合对迭代器模式的实现) - [4. `LinkedList`集合对迭代器模式的实现](#4.
LinkedList
集合对迭代器模式的实现) - [5. 迭代器与增强for循环](#5. 迭代器与增强for循环)
- [1. `Iterable`接口](#1.
- 五、优缺点
一、介绍
迭代器模式(Iterator Pattern
) ,属于行为型设计模式,在java
和python
中十分常见。目的是在不暴露集合内部结构的条件下,顺序访问该集合内部的元素。
提供一种顺序访问一个集合中所有元素的方式, 而又无需暴露该对象的内部表示。
在需要顺序访问集合元素的场景中,传统方式是通过以下方式对集合元素进行遍历
java
for(int i=0; i<list.size(); i++) {
Object obj = list.get(i);
}
在该方式中,对元素的获取是通过直接调用集合的get()
方法完成的,即对元素的遍历由集合本身负责。
而使用迭代器设计模式后,集合本身不负责元素的遍历,而提供一个获取迭代器的方法iterator()
,对元素的遍历由迭代器负责,如下所示
java
Iterator<Object> iterator = list.iterator();
while(iterator.hasNext()) {
Object obj = iterator.next();
}
这是一种对责任细分的体现,集合将对元素遍历的责任交给迭代器完成。
迭代器的使用也十分简单,一般来讲,只提供两个方法:hasNext()
和next()
。hasNext()
方法用于遍历集合,next()
方法用于顺序获取集合中的元素。
二、迭代器模式中的角色
在迭代器模式中,所有类型的集合(无论底层是数组还是链表),都需要提供一个获取迭代器的方法,因此我们可以将该方法抽象出来封装到一个独立的**抽象接口Iterable
**中,实现该接口的任何集合都具备获取迭代器的能力。
迭代器无论采取什么样的遍历方式,都需要两个基本方法hasNext()
和next()
,因此我们将这两个方法抽象到接口类Iterator
中。
因此进过分析,可以确定在迭代器模式中,存在四个基本角色:支持迭代的接口类 、具体集合类 、迭代器抽象接口类 、具体迭代器类。
-
支持迭代的集合抽象接口(
BarIterable
)该接口定义一个获取迭代器的方法
iterator()
,实现该接口的所有集合类都需要实现对应的逻辑。另外,在声明该抽象接口时,还需要在接口上声明一个泛型
<T>
,因为这是一个集合接口,意味着集合中的元素可以是任意类型。同理,iterator()
方法返回的迭代器对象也应该标注泛型<T>
。 -
具体的集合类(
BarList
)实现抽象接口(
BarIterable
) ,按照本身的实际情况对iterator()
方法进行实现。与上面接口类
BarIterable
类似,该集合类和其实现的iterator()
方法也应该各声明一个泛型<T>
。 -
迭代器抽象接口(
FooIterator
)该接口定义了迭代器的基本行为,遍历和获取,分别用
hasNext()
方法和next()
方法表示。作为迭代器,也应该在类上声明一个泛型
<T>
,原因同上。因此其next()
方法的返回值也应该是泛型<T>
。 -
迭代器具体实现类(
FooItr
)实现迭代器抽象接口
FooIterator
。由于迭代器的功能是对集合中的元素进行遍历,因此我们常用的做法是将具体迭代器声明为集合类的内部类,这样迭代器就可以直接访问集合中的元素了。
因此迭代器模式的通用UML图如下所示
三、代码演示
根据以上对迭代器模式中各个角色的分析,我们使用代码对其进行演示
1. 支持迭代的集合抽象接口(BarIterable
)
新建接口类BarIterable
,并定义iterator()
方法,同时声明接口类的泛型<T>
。
java
public interface BarIterable<T> {
/** 获取迭代器 **/
FooIterator<T> iterator();
}
2. 具体的集合类(BarList
)
新建集合类BarList
,实现接口BarIterable
。
java
public class BarList<T> implements BarIterable<T> {
private final Object[] array = new Object[]{1,2,3,4,5,6,7,8,9};
@Override
public FooIterator<T> iterator() {
return new FooItr();
}
}
3. 迭代器抽象接口(FooIterator
)
新建迭代器接口类FooIterator
,并定义hasNext()
方法和next()
方法,同时声明泛型<T>
。
java
public interface FooIterator<T> {
boolean hasNext();
T next();
}
4. 迭代器具体实现类(FooItr
)
新建迭代器实现类FooItr
,实现接口类FooIterator
。
由于迭代器的功能是对集合中的元素进行遍历,因此我们常用的做法是将具体迭代器声明为集合类的内部类,这样迭代器就可以直接访问集合中的元素了。
下面我们在集合类BarList
中定义该内部类
java
public class BarList<T> implements BarIterable<T> {
private final Object[] array = new Object[]{1,2,3,4,5,6,7,8,9};
@Override
public FooIterator<T> iterator() {
return new FooItr();
}
private class FooItr implements FooIterator<T> {
private Integer index = 0;
@Override
public boolean hasNext() {
return index < array.length;
}
@Override
public T next() {
return (T) array[index++];
}
}
}
在该内部类中,我们通过一个属性index
-
index
属性用来访问集合中的数组结构,作为数组下标从数组中读取数据。
-
hasNext()
方法数组中长度是固定的,根据当前
index
判断是否已经到达最后一个元素 -
next()
方法直接通过
index
作为数组下标,从数组中读取数据。
5. 代码测试
新建一个测试类IteratorDemo
,在main()
方法中测试代码
java
public class IteratorDemo {
public static void main(String[] args) {
BarList<Integer> barList = new BarList<>();
FooIterator<Integer> iterator = barList.iterator();
while (iterator.hasNext()) {
Integer integer = iterator.next();
System.out.print(integer + " ");
}
}
}
运行后输出如下所示
四、java中迭代器模式的应用
在java的集合框架中,我们都知道所有的集合类都实现了Collection
接口,但Collection
接口并不是集合的顶级接口 ,Iterable
接口才是,也就是说,所有的集合类都实现了迭代器模式。下面是java集合框架的类图
我们以ArrayList
和LinkedList
为例,按照上面我们对迭代器模式各个角色的分析,来看一下java集合是如何应用迭代器模式的
1. Iterable
接口
Iterable
接口如下所示,它定义了一个获取迭代器的方法iterator()
java
public interface Iterable<T> {
/** 获取迭代器 */
Iterator<T> iterator();
default void forEach(Consumer<? super T> action) {
Objects.requireNonNull(action);
for (T t : this) {
action.accept(t);
}
}
default Spliterator<T> spliterator() {
return Spliterators.spliteratorUnknownSize(iterator(), 0);
}
}
2. 迭代器抽象接口Iterator
java
public interface Iterator<E> {
/** 是否到达集合中最后一个元素 */
boolean hasNext();
/** 获取集合中下一个元素 */
E next();
default void remove() {
throw new UnsupportedOperationException("remove");
}
default void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
while (hasNext())
action.accept(next());
}
}
3. ArrayList
集合对迭代器模式的实现
java
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
transient Object[] elementData;
// 省略无关代码
// 获取迭代器
public Iterator<E> iterator() {
return new Itr();
}
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;
Itr() {}
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();
// 数组下标+1
cursor = i + 1;
return (E) elementData[lastRet = i];
}
public void remove() {
// ...
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
// ...
}
final void checkForComodification() {
// ...
}
}
}
4. LinkedList
集合对迭代器模式的实现
java
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
{
transient Node<E> first;
transient Node<E> last;
// 获取迭代器
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
// ListIterator接口继承于Iterator接口,对Iterator接口定义的方法进行补充
private class ListItr implements ListIterator<E> {
private Node<E> lastReturned;
private Node<E> next;
private int nextIndex;
private int expectedModCount = modCount;
// 省略无关代码
public boolean hasNext() {
return nextIndex < size;
}
public E next() {
checkForComodification();
if (!hasNext())
throw new NoSuchElementException();
lastReturned = next;
// next.next获取下一个元素
next = next.next;
nextIndex++;
return lastReturned.item;
}
}
}
5. 迭代器与增强for循环
我们遍历一个集合时,最常使用的方式就是增强for
循环 了,即for(T t : 集合)
,那么它和迭代器有什么关系呢?
我们通过增强for
循环演示一段代码
java
public class Demo {
public static void main(String[] args) {
List<String> list = Arrays.asList("a", "b", "c", "d", "e", "f");
for (String s : list) {
System.out.print(s + " ");
}
}
}
输出结果为:
下面我们看一下java对该类所编译出的class文件
从该class文件中我们可以看到,我们在java文件中编写的增强for循环 在编译过程中被转换成了迭代器。
五、优缺点
优点:
- 简化了集合类。集合类中遍历元素的责任转移给迭代器了。
- 在一个集合中可以存在多种遍历方式,这取决于你创建了多少个内部的迭代器实现类。
缺点:
- 一种遍历方式对应一个迭代器类,这将增加系统中类的数量
纸上得来终觉浅,绝知此事要躬行。
------------------------我是万万岁,我们下期再见------------------------