java集合类中的迭代器模式

时间:2022-10-30 17:03:05

不说模式的问题,看一个<<设计模式之禅>>里面的例子。
老板要看到公司了各个项目的情况。(我知道我这个概述很让人头大,看代码吧)

示例程序

v1

package Iterator;

/**
* @author cbf4Life cbf4life@126.com
* 定义一个接口,所有的项目都是一个接口
*/
public interface IProject {

//从老板这里看到的就是项目信息
public String getProjectInfo();
}
package Iterator;

/**
* @author cbf4Life cbf4life@126.com
* 所有项目的信息类
*/
public class Project implements IProject {
//项目名称
private String name = "";

//项目成员数量
private int num = 0;

//项目费用
private int cost = 0;

//定义一个构造函数,把所有老板需要看到的信息存储起来
public Project(String name,int num,int cost){
//赋值到类的成员变量中
this.name = name;
this.num = num;
this.cost=cost;
}

//得到项目的信息
public String getProjectInfo() {
String info = "";

//获得项目的名称
info = info+ "项目名称是:" + this.name;
//获得项目人数
info = info + "\t项目人数: "+ this.num;
//项目费用
info = info+ "\t 项目费用:"+ this.cost;

return info;
}

}

package Iterator;

import java.util.ArrayList;

/**
* @author cbf4Life cbf4life@126.com
* 老板来看项目信息了
*/
public class Boss {

public static void main(String[] args) {
//定义一个List,存放所有的项目对象
ArrayList<IProject> projectList = new ArrayList<IProject>();

//增加星球大战项目
projectList.add(new Project("星球大战项目",10,10));
//增加扭转时空项目
projectList.add(new Project("扭转时空项目",100,1000));
//增加超人改造项目
projectList.add(new Project("超人改造项目",10000,100000));

//这边100个项目
for(int i=4;i<10;i++)
projectList.add(new Project("第"+i+"个项目",i*5,i*100));

//遍历一下ArrayList,把所有的数据都取出
for(IProject project:projectList)
System.out.println(project.getProjectInfo());
}
}


类图如下:
java集合类中的迭代器模式
实在太简单了,没什么说的。
不过现在,我想提几个问题。
多个对象聚在一起形成的总体叫做聚集(Aggregate)。在java中就是实现了Collection接口的那些类。我们在学习c的时候就接触的数组就是最最基本的聚集,在java中,数组也是其他聚集类型的设计基础。
从1.2版本开始java就提供了多种聚集,Vector,ArrayList,Stack,LinkList,HashSet,TreeSet,HashMap,TreeMap等等。
数组,堆栈,散列表有多种存储结构,如果我不想有数组来存储工程信息了要换成散列表,上面的代码就得大改(其实,我得承认这个假设本身就有问题,很少会有人没事闲得慌改存储结构,我们只是举个例子,引出我们下面要说的问题)
我们希望,java中的各种聚集都能提供一种"迭代"的功能。
ok 看我们的下个版本


从图中能看到,我们已经使用了jdk提供的迭代器相关模块。

v2

public interface Iterable<T> {
Iterator<T> iterator();
}
Iterable接口,任何能"具有迭代"功能的聚集都要实现这个接口,它也只有一个方法,返回聚集的迭代器。

public interface Iterator<E> {

boolean hasNext();
E next();
void remove();
}
Iterator接口,就是具体的迭代器。
大家在理解迭代器模式的时候,不妨这样想,一个聚集能够遍历自身,之前是因为我们用for循环依次取出每个元素,现在引入了模式后,遍历的功能就交给迭代器这个类(接口)了。
现在看代码

package iterator.v2;

import java.util.ArrayList;
import java.util.Iterator;

/**
* @author cbf4Life cbf4life@126.com
* 定义一个迭代器
*/
public class ProjectIterator implements Iterator<Object>{

//所有的项目都放在这里ArrayList中
private ArrayList<IProject> projectList = new ArrayList<IProject>();

private int currentItem = 0;

//构造函数出入projectList
public ProjectIterator(ArrayList<IProject> projectList){
this.projectList = projectList;
}

//判断是否还有元素,必须实现
public boolean hasNext() {
//定义一个返回值
boolean b = true;
if(this.currentItem>=projectList.size() || this.projectList.get(this.currentItem) == null){
b =false;
}
return b;
}

//取得下一个值
public IProject next() {
return (IProject)this.projectList.get(this.currentItem++);
}

//删除一个对象
public void remove() {
//暂时没有使用到
}

}
package iterator.v2;

import java.util.Iterator;

/**
* @author cbf4Life cbf4life@126.com
* 定义一个接口,所有的项目都是一个接口
*/
public interface IProject extends Iterable<Object>{

//增加项目
public void add(String name,int num,int cost);

//从老板这里看到的就是项目信息
public String getProjectInfo();

public Iterator<Object> iterator();
}
package iterator.v2;

import java.util.ArrayList;
import java.util.Iterator;

/**
* @author cbf4Life cbf4life@126.com
* 所有项目的信息类
*/
@SuppressWarnings("all")
public class Project implements IProject {
//定义一个项目列表,说有的项目都放在这里
private ArrayList<IProject> projectList = new ArrayList<IProject>();
//项目名称
private String name = "";
//项目成员数量
private int num = 0;
//项目费用
private int cost = 0;

public Project(){

}

//定义一个构造函数,把所有老板需要看到的信息存储起来
private Project(String name,int num,int cost){
...
}

//增加项目
public void add(String name,int num,int cost){
this.projectList.add(new Project(name,num,cost));
}

//得到项目的信息
public String getProjectInfo() {
...
}

//产生一个遍历对象
public Iterator<Object> iterator(){
return new ProjectIterator(this.projectList);
}

}
package iterator.v2;


/**
* @author cbf4Life cbf4life@126.com
* 老板来看项目信息了
*/
public class Boss {

public static void main(String[] args) {
//定义一个List,存放所有的项目对象
IProject project = new Project();

//增加星球大战项目
project.add("星球大战项目ddddd",10,100);
//增加扭转时空项目
project.add("扭转时空项目",100,10000);
//增加超人改造项目
project.add("超人改造项目",10000,1000000);

//这边100个项目
for(int i=4;i<14;i++){
project.add("第"+i+"个项目",i*5,i*1000);
}

//遍历一下ArrayList,把所有的数据都取出
ProjectIterator projectIterator = (ProjectIterator) project.iterator();
while(projectIterator.hasNext()){
IProject p = (IProject)projectIterator.next();
System.out.println(p.getProjectInfo());

}

}
}


迭代器模式的通用类图
java集合类中的迭代器模式

图里面的具体迭代器与具体聚集之间的关系有点意思,这一点希望大家多看看代码。
另一方面,我们也可以把ProjectIterator作为内部类放置在projcet类里,这样迭代器就能直接访问到projectList。
后面我们这样改了之后再boss类里面
ProjectIterator projectIterator = (ProjectIterator) project.iterator();
代码依然可以用,这就是创建与使用分离的好处,也就是工厂模式的好处。

jdk的迭代器模式在聚集中的应用

uml图

java集合类中的迭代器模式

 
 在AbstractList中有以下两个方法
    public ListIterator<E> listIterator() {
return new ListItr(0);
}


public Iterator<E> iterator() {
return new Itr();
}

不管是上面的uml图还是下面的代码,大家现在心里一定有个问题,ListItr与Itr是怎么回事。

其实我们只要看看AbstractList的源代码就能明白:Iterator接口里面的方法实在是太少了,hasNext,next,remove就三个方法;从遍历上来说也就是正向遍历,我要是想逆向遍历一下怎么办?ListItr就有这个功能
下面再看一段代码:
import java.util.*;

public 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("张无忌");
persons.add("小龙女");
persons.add("令狐冲");
persons.add("韦小宝");
persons.add("袁紫衣");
persons.add("小龙女");

process(persons);
}
}


我们现在是用ArrayList来存储数据,要是想换成HashSet很简单,也就
persons = new HashSet<String>(); //创建一个ArrayList类型的聚合对象
一行代码的事。

下面还有一个问题,一个班级(Class in School)包含多个学生(Student),使用Java内置迭代器实现对学生信息的遍历,要求按学生年龄由大到小的次序输出学生信息。
怎么做,这个咱们下一节来讨论。

参考资料

java与模式
设计模式之禅 第二十章 迭代器模式
http://blog.csdn.net/lovelion/article/details/9992005 遍历聚合对象中的元素——迭代器模式            (很多资料都是参考自刘伟老师的博客)