概述
迭代器模式如此常用,以至于很多编程语言都默认实现了此模式,所以虽然我们天天都在用迭代器模式,但却很少有自己实现的机会。
其核心动机是为了在迭代一个容器对象的同时不暴露其内部细节,这啥意思呢?
例如ArrayList
类,当我们需要迭代它的元素的时候,我们不可能将其内部存储元素用的数据结构,例如数组,暴露给用户。那样的话不仅将来更换数据结构变的不可能,而且如果将内部细节暴露给了客户端,那么就无法控制客户端对其行为了。
定义
提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。
使用场景
在java开发中,当你要你要实现自己的容器类,且要迭代里面的元素时。换句话还可以说,当你要使自己的类支持foreach
操作的时候使用此模式。
在Java中没有必要自己实现这个模式,因为Java类库默认支持了此模式,所以此处我们也以Java官方类库为例谈论一下,因为我想写一些有用的东西,而不是华而不实的东西。
实例
在我们上大学的时候,无论哪一科的老师都喜欢在课前点名,为什么会这样呢?是因为老师不知道自己班级有几名学生吗?当然不是,恰恰是因为老师明知道自己班级有多少学生,但放眼望去到场的却寥寥无几… 毕业多年,现在想想真的很怀念自己的大学生活,只恨当年为什么不拼命学习,拼命做一些有意义的事,我们的青春啊…
老师点名肯定是从头点到尾,对班里的每一个学生进行迭代…
相应接口
由于Java已经天然支持了迭代器模式,我们直接使用就好了。主要涉及如下两个接口,看起来好简单对不对?对啊,本来也没多难。不过大概10年前吧,我第一次从C#中看到这两类似的玩意还是没有理解了。哎, 又应了那句老话:在老鸟眼里的土坷垃就是菜鸟眼前的一座大山啊!
public interface Iterator<E> {
boolean hasNext();
E next();
//java8后加入的default方法
...
}
public interface Iterable<T> {
Iterator<T> iterator();
//java8后加入的default方法
...
}
如果我们要使自己的类支持迭代功能,只需要实现Iterable
接口,而这个接口仅仅是要求实现一个方法,提供一个迭代器。而这个迭代器要实现Iterator
接口,所以我们唯一需要做的就是提供一个实现了Iterator
接口的类而已。
实现
如下所示,我写了一个Class
类,它实现了Iterable
接口。然后内部类Itr
作为迭代器。此类中我使用ArrayList
作为内部数据结构,后期也可以换成数组,链表,树,图等等,而使用者却不用关心这些内部表示,这就是迭代器的妙处所在。
public class Class implements Iterable<Student> {
private final List<Student> students = new ArrayList<>();
public Class() {
students.add(new Student("王二狗", 28));
students.add(new Student("牛翠花", 20));
students.add(new Student("林蛋大", 29));
}
public boolean addStudent(Student student){
return students.add(student);
}
public boolean removeStudent(Student student){
return students.remove(student);
}
@Override
public Iterator<Student> iterator() {
return new Itr();
}
private class Itr implements Iterator<Student> {
int index = 0;
@Override
public boolean hasNext() {
if (index < students.size()) {
return true;
}
return false;
}
@Override
public Student next() {
Student student = students.get(index);
index++;
return student;
}
}
}
客户端使用
相信有过点Java编程经验的用起这个类时都非常得心应手,因为他不知道已经用了多少次了。获取Class
对象的迭代器,然后使用while
循环迭代即可。
public class IteratorClient {
public void checkAttendance(){
Class cls= new Class();
System.out.println("--------------开始点名--------------");
Iterator<Student> iterator= cls.iterator();
while (iterator.hasNext()){
System.out.println(iterator.next());
}
}
}
输出:
--------------开始点名--------------
Student{name='王二狗', age=28}
Student{name='牛翠花', age=20}
Student{name='林蛋大', age=29}
值得注意的是,我们也可以对Class
类使用foreach
循环
for (Student cl : cls) {
System.out.println(cl);
}
技术要点总结
你要使你的类可以迭代(支持foreach),就实现java.lang.Iterable
接口。
遍历Collection的两种方式:
① 使用迭代器Iterator
② foreach循环(或增强for循环)
2.1说明:
Iterator对象称为迭代器(设计模式的一种),主要用于遍历 Collection 集合中的元素。
GOF给迭代器模式的定义为:提供一种方法访问一个容器(container)对象中各个元素,而又不需暴露该对象的内部细节。迭代器模式,就是为容器而生。
2.2作用:遍历集合Collectiton元素
2.3如何获取实例:coll.iterator()返回一个迭代器实例
2.4遍历的代码实现:
Iterator iterator = coll.iterator();
//hasNext():判断是否还下一个元素
while(iterator.hasNext()){
//next():①指针下移 ②将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
2.5图示说明:
2.6 remove()的使用:
//测试Iterator中的remove()
//如果还未调用next()或在上一次调用 next 方法之后已经调用了 remove 方法,再调用remove都会报IllegalStateException。
//内部定义了remove(),可以在遍历的时候,删除集合中的元素。此方法不同于集合直接调用remove()
@Test
public void test3 () {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry", 20));
coll.add(new String("Tom"));
coll.add(false);
//删除集合中"Tom"
Iterator iterator = coll.iterator();
while (iterator.hasNext()) {
// iterator.remove();
Object obj = iterator.next();
if ("Tom".equals(obj)) {
iterator.remove();
// iterator.remove();
}
}
//遍历集合
iterator = coll.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}
}
3.jdk5.0新特性–增强for循环:(foreach循环)
1.遍历集合举例:
@Test
public void test1 () {
Collection coll = new ArrayList();
coll.add(123);
coll.add(456);
coll.add(new Person("Jerry", 20));
coll.add(new String("Tom"));
coll.add(false);
//for(集合元素的类型 局部变量 : 集合对象)
for (Object obj : coll) {
System.out.println(obj);
}
}
说明:
内部仍然调用了迭代器。
2.遍历数组举例:
@Test
public void test2 () {
int[] arr = new int[]{1, 2, 3, 4, 5, 6};
//for(数组元素的类型 局部变量 : 数组对象)
for (int i : arr) {
System.out.println(i);
}
}