JAVA设计模式之迭代器模式

一 概述

迭代器模式如此常用,以至于很多编程语言都默认实现了此模式,所以虽然我们天天都在用迭代器模式,但却很少有自己实现的机会。

其核心动机是为了在迭代一个容器对象的同时不暴露其内部细节,这啥意思呢?

例如 ArrayList 类,当我们需要迭代它的元素的时候,我们不可能将其内部存储元素用的数据结构,例如数组,暴露给用户。那样的话不仅将来更换数据结构变的不可能,而且如果将内部细节暴露给了客户端,那么就无法控制客户端对其行为了。

1.1 定义

提供一种方法顺序访问一个容器对象中的各个元素,而又不需要暴露该对象的内部表示。

  • 迭代器模式属于行为型模式
  • 迭代器(Iterator)模式,又叫做游标(Cursor)模式
  • Java 中的 Map、List 等等容器,都使用到了迭代器模式

1.2 使用场景

在 Java 开发中,当你要你要实现自己的容器类,且要迭代里面的元素时。换句话还可以说,当你要使自己的类支持 foreach 操作的时候使用此模式。

在 Java 中没有必要自己实现这个模式,因为 Java 类库默认支持了此模式,所以此处我们也以 Java 官方类库为例谈论一下,因为我想写一些有用的东西,而不是华而不实的东西。

1.3 UML 类图

在这里插入图片描述
角色说明:

  • Iterator(迭代器接口):负责定义、访问和遍历元素的接口
  • ConcreteIterator(具体迭代器类):实现迭代器接口
  • Aggregate(容器接口):定义容器的基本功能以及提供创建迭代器的接口
  • ConcreteAggregate(具体容器类):实现容器接口中的功能
  • Client(客户端类):即要使用迭代器模式的地方

二 实现

在我们上大学的时候,无论哪一科的老师都喜欢在课前点名,为什么会这样呢?是因为老师不知道自己班级有几名学生吗?当然不是,恰恰是因为老师明知道自己班级有多少学生,但放眼望去到场的却寥寥无几。。。

老师点名肯定是从头点到尾,对班里的每一个学生进行迭代。

2.1 创建迭代器接口

由于 Java 已经天然支持了迭代器模式,我们直接使用就好了。主要涉及如下两个接口,看起来好简单对不对?对啊,本来也没多难。

public interface Iterator<E> {
    boolean hasNext();
    E next();

   // java8 后加入的 default 方法
    ...
}

public interface Iterable<T> {
    Iterator<T> iterator();
    
   //java8 后加入的 default 方法
    ...    
}

如果我们要使自己的类支持迭代功能,只需要实现 Iterable 接口,而这个接口仅仅是要求实现一个方法,提供一个迭代器。而这个迭代器要实现 Iterator 接口,所以我们唯一需要做的就是提供一个实现了 Iterator 接口的类而已。

2.2 实现

如下所示,我写了一个 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;
        }
    }
}

2.3 客户端使用

相信有 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);
 }

三 总结

3.1 应用场景

  • 遍历一个容器对象时
  • 实际我们开发中很少使用到迭代器模式。虽然不怎么用得到,但是了解其原理能够让我们在看到相关的源码(如 Java 中的 Map、List 等等容器)时能够更容易了解源码的相关思想

3.2 优点

  • 可以支持以不同的方式去遍历容器对象,如顺序遍历,逆序遍历等等
  • 符合单一职责原则,容器类与遍历算法的分离,不同类负责不同的功能

3.3 缺点

  • 会产生多余的对象,消耗内存
  • 会增多类文件
  • 遍历过程是一个单向且不可逆的遍历
  • 遍历过程需要注意容器是否改变,若期间改变了,可能会抛出异常

四 Android中的源码实例分析

除了 Java 中的 Map、List 等有用到迭代器模式之外,Android 中使用数据库查询时返回的 Cursor 游标对象,实际上就是使用了迭代器模式来实现,首先先让我们来看下怎么使用 Cursor:

4.1 使用 Cursor

// 各种参数请自行查询用法,这里就不说明了
        Cursor cursor = sqLiteDatabase.query("table", null, null, null, null, null, null);
        if (cursor.moveToFirst()) { // 游标指向第一行
            do {
                cursor.getInt(0); // 获得第一列数据
                cursor.getString(1); // 获得第二列数据
            } while (cursor.moveToNext()); // 移到下一行
        }

4.2 Cursor 源码

// Cursor 是一个接口,实际上就是迭代器接口
public interface Cursor extends Closeable {
    // 其他代码略
    boolean moveToFirst();
    int getInt(int columnIndex);
    String getString(int columnIndex);
    boolean moveToNext();
}

从上面代码可以看出 Cursor 游标实际上就是个迭代器。

4.3 其他

有兴趣的可以去看下 Java 中的 Map、List 等的源码,那个更直观简单。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值