1、什么是迭代器呢?
迭代器是一种取出元素的方式,也就是遍历某一元素集合内元素的方式。我们平常对于数组的遍历方式是通过下角标利用for循环等来操作数据,元素集合不止数组一种,例如还有ArrayList(底层为数组)、LinkedList(底层为链表)、HashSet(使用hash表)等,每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么针对这些集合遍历就要分别使用对应的遍历方式。
那么,有没有一个统一的解决方案来处理这样的循环遍历问题呢?我们只提供一种模式就能够遍历所有的不同数据结构的集合,而且还不能够暴露集合内部的表示(即不用知道内部实现细节到底是数组还是ArrayList、LinkedList,这样就不必在外部应用中暴露获得元素的方式),这就需要迭代器设计模式闪亮登场了。
迭代器模式的出现统一了数据循环遍历的问题,让我们不再关心软件系统内部的实现细节,按照统一的模式顺序访问一个集合对象中的各个元素。
2、迭代器模式的方式?
因为每个集合对象本身不一样,所以当我们对不同类型的集合进行遍历时,由于必须暴露其内部表示,因此所写的遍历语句(for)是不能通用的。如果我们对需要用到的集合创建一个迭代器,由于对迭代器的遍历语句是相同的,那么就可以实现遍历语句的通用性。另外,拥有了迭代器就意味着我们对集合的实现方式是完全不知道的,即不必知道集合内部的实现细节,以后一旦需要改变集合的实现,例如从数组改为ArrayList,客户端的代码是不需要发生任何改变的,甚至于客户端根本就不知道所遍历的集合发生了改变。
对于遍历的方式,首先谈一下Java语言内置的迭代器,即集合中的迭代器,其是耦合在集合对象内部,让容器实现遍历功能,这种方式应用在Java集合的底层代码实现中;
Java语言内置的迭代器是在外面定义一个迭代器的接口Iterator,这样所有的集合在其内部定义迭代器时统一实现该接口,这样所有集合内部都有了自己的迭代器,单列集合的根接口Collection中定义一个获取迭代器的方法public Iterator iterator()专门用于获取其迭代器对象,这样所有实现Collection接口的实现类中只需要重写该iterator方法,返回自己的迭代器对象即可。
这种方式存在一定的弊端:首先,将遍历算法耦合在集合对象内部,这就加重了几何对象的负担,不但需要处理数据的添加、删除和修改,还要负责数据的遍历,显然,集合对象的负担过重,这也是不符合单一职责原则的。
另外一种方式就是今天我们需要讲的一种方式:
这需要满足以下两点内容:
(1)迭代器在不需要暴露集合对象内部实现细节的情况下,能够遍历集合中的数据;
(2)迭代器获取集合对象内部数据的方式,也不需要依赖于集合对象下标,即生成迭代器之后,就完全脱离集合对象的约束。
在迭代器的遍历过程中,我们没有暴露集合类的内部细节内容,而是返回object类型,而遍历方法也是不需要任何下标信息的,因为我们可以在迭代器内部获得集合对象的引用,可以默认下标从-1开始,在遍历方法中顺序变化,从而获得集合对象内部元素内容。这样,就有效的将迭代器与集合对象分离开,使两者从产生迭代器之后就各不相干,而客户端调用只与迭代器遍历相关,不再依赖于集合对象的内部细节。
迭代器建模图:
3、迭代器模式案例?
需求:对人员信息使用迭代器模式进行遍历。
(1)首先得到人员信息接口,里面只有一个获得人员信息的方法:
<span style="font-size:18px;">package 迭代器模式;
/**
* 需求:人员信息接口
* 方法:获得人员信息
* @author win2016
*
*/
public interface IPerson {
//获得人员信息方法
public String getPersonInfo();
}</span>
(2)人员信息类,用于存储人员的基本信息:
<span style="font-size:18px;">package 迭代器模式;
/**
* 需求:人员类
*
* @author win2016
*
*/
public class Person implements IPerson {
//姓名
private String name;
//年龄
private int age;
//性别
private int sex;
//构造方法传入值
public Person(String name,int age,int sex){
this.name = name;
this.age = age;
this.sex = sex;
}
//获取人员信息
@Override
public String getPersonInfo() {
return "姓名:"+this.name+"-年龄"+this.age+"-性别"+(this.sex==1?"男":(this.sex==0?"女":""));
}
}</span>
(3)人员集合接口,有两个方法,一个是获得人员内部信息列表方法,一个是获得迭代器接口方法,设置两种方法是为了在客户端应用中分别使用,便于比较。
<span style="font-size:18px;">package 迭代器模式;
import java.util.ArrayList;
/**
* 需求:人员集合接口 获得内部存储人员信息内容
* 方法:获得人员内部信息列表方法;获得迭代器接口方法(目的:在客户端应用中分别使用)
* @author win2016
*
*/
public interface IPersonList {
//获得内部存储人员信息内容
public ArrayList<IPerson> getPersonList();
//迭代器
public Iterator1 iterator();
}</span>
(4)人员集合实现:
<span style="font-size:18px;">package 迭代器模式;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Random;
/**
* 需求:人员集合实现类
* 在PersonList的构造方法中初始化人员信息列表内容
*
* @author win2016
*
*/
public class PersonList implements IPersonList {
//存储用户信息集合
private ArrayList<IPerson> list = new ArrayList<IPerson>();
//构造方法初始化人员信息
public PersonList(){
Random random = new Random();
//创建人员信息
for (int i = 0; i < 6; i++) {
IPerson person = new Person("人员"+i,random.nextInt(30),random.nextInt(2));
//存入集合
list.add(person);
}
}
//获得内部人员信息
public ArrayList<IPerson> getPersonList(){
return this.list;
}
//迭代器
public Iterator1 iterator(){
return new PersonIterator(this.list);
}
}</span>
(5)迭代器接口:
<span style="font-size:18px;">package 迭代器模式;
/**
* 需求:迭代器接口
* 方法:判断是否存在下一个元素;获取下一个元素方法;删除方法
* @author win2016
*
*/
public interface Iterator1 {
//判断是否有下一个节点
public boolean hasNext();
//获得下一个节点对象
public Object next();
//删除方法
public Object remove();
}</span>
(6)迭代器实现类:
<span style="font-size:18px;">package 迭代器模式;
import java.util.ArrayList;
/**
* 需求:迭代器实现类
*
* @author win2016
*
*/
public class PersonIterator implements Iterator1 {
//存储人员列表对象信息
private final ArrayList<IPerson> personList;
//存储位置信息,初始值为-1
private int index = -1;
//构造方法将人员列表对象传入
public PersonIterator(ArrayList<IPerson> personList){
this.personList = personList;
}
//是否有下一个元素
@Override
public boolean hasNext() {
return (this.personList == null? false:(index<this.personList.size()-1));
}
//获得下一个元素对象
@Override
public Object next() {
return this.personList.get(++index);
}
//删除对象
@Override
public Object remove() {
if (this.personList != null) {
return this.personList.remove(index);
}
return null;
}
}</span>
<span style="font-size:18px;">package 迭代器模式;
/**
* 需求:测试类
* @author win2016
*
*/
public class Test {
public static void main(String[] args) {
//创建人员列表对象
IPersonList personList = new PersonList1();
System.out.println("--------使用迭代器输出人员信息----------");
//生成迭代器
Iterator1 iterator = personList.iterator();
//循环迭代器,遍历每一个元素输出人员信息
while(iterator.hasNext()){
//获得人员对象实例
IPerson person = (IPerson) iterator.next();
if (person!=null) {
//输出人员信息
System.out.println(person.getPersonInfo());
}
}
}
}</span>
上面是使用迭代器迭代了ArrayList集合。
那么,迭代器如何面对变化呢?
现在我们假设人员列表信息类型改变了,使用数组类型而不是ArrayList,该如何处理呢?我们只要增加一个用数组实现的PersonList1:
<span style="font-size:18px;">package 迭代器模式;
import java.util.ArrayList;
import java.util.Random;
/**
* 需求:数组类型
* @author win2016
*
*/
public class PersonList1 implements IPersonList {
//存储用户信息列表
private final IPerson[] list = new IPerson[6];
//构造方法初始化人员信息
public PersonList1(){
Random random = new Random();
//创建人员信息
for (int i = 0; i < 6; i++) {
IPerson person = new Person("人员"+i, random.nextInt(30), random.nextInt(2));
list[i] = person;
}
}
//迭代器
public Iterator1 iterator(){
return new ArrPersonIterator(this.list);
}
//获得内部存储人员信息内容
public IPerson[] getPersonInfo() {
return list;
}
@Override
public ArrayList<IPerson> getPersonList() {
// TODO Auto-generated method stub
return null;
}
}</span>
然后再增加一个数组迭代器:
<span style="font-size:18px;">package 迭代器模式;
/***
* 需求:数组迭代器
*
* @author win2016
*
*/
public class ArrPersonIterator implements Iterator1 {
//私有属性存储人员列表对象信息
private final IPerson[] personList;
//存储位置信息,初始值为-1
private int index = -1;
//构造方法将人员列表对象存入
public ArrPersonIterator(IPerson[] personList){
this.personList = personList;
}
//判断是否有下一个节点
public boolean hasNext(){
return (this.personList == null?false:(index<this.personList.length-1));
}
//获取下一个节点元素
public Object next(){
if (this.personList!=null&&(index<this.personList.length-1)) {
//获得人员列表对象中的人员信息
return this.personList[++index];
}
return null;
}
//删除对象
public Object remove(){
if (this.personList!=null) {
IPerson person = this.personList[index];
this.personList[index] = null;
return person;
}
return null;
}
}</span>
这样就可以啦。
总结:当你需要访问一个聚合对象,而且不管这些对象是什么都需要遍历的时候,就应该考虑使用迭代器模式。另外,当需要对聚集有多种方式遍历时,可以考虑去使用迭代器模式。迭代器模式为遍历不同的聚集结构提供如开始、下一个、是否结束、当前哪一项等统一的接口。