行为篇-迭代器模式

Posted zhixuChen333

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了行为篇-迭代器模式相关的知识,希望对你有一定的参考价值。

文章目录


前言

迭代,在程序中特指对某集合中各元素逐个取用的行为。迭代器模式(Iterator)提供了一种机制来按顺序访问集合中的各元素,而不需要知道集合内部的构造。换句话讲,迭代器满足了对集合迭代的需求,并向外部提供了一种统一的迭代方式,而不必暴露集合的内部数据结构。


提示:以下是本篇文章正文内容,下面案例可供参考

一、物以类聚

迭代的过程是基于一系列数据展开的,所以集合是不得不提的概念。物以类聚,集合是由一个或多个确定的元素构成的整体,其实就是把一系列类似的元素按某种数据结构集结起来,作为一个整体来引用,以便于维护。简单来讲,可以把集合理解为“一堆”或者“一群”类似的元素集结起来的整体。为了承载不同的数据形式,集合类提供了多种多样的数据结构,如我们常用的ArrayList、HashSet、HashMap等,

每种集合都有不同的特性,可以满足对各种数据结构的承载需求。有了集合才会产生对其迭代的需求,而每种数据结构的迭代方式又不尽相同,所以,定义标准化的迭代器势在必行,以提供统一、通用的使用方法。

二、遍历标准化

不同的数据结构需要不同的集合类,而针对不同的集合类的迭代方式也不尽相同,如for、foreach、while,甚至Java8引入的流式遍历等。举个例子,我们可以使用传统的for循环对List集合进行迭代遍历,而对于Set集合,for循环就无能为力了,这是因为Set本身集合不存在index索引号,所以必须用foreach循环(迭代器Iterator)进行遍历。难道就没有一种通用的迭代标准,能让调用者使用统一的方式进行遍历吗?如果我们深究源码就会发现,Collection接口中有这样一个接口。

Collection接口的一段源码,可以看到其明确声明了获取迭代器的接口iterator(),通过调用这个接口就可以返回标准的迭代器对象。既然有了这种标准,那么Collection这集合类就可以实现自己的迭代器。而Map集合也可以按照同样的方式,从其EntrySet中获取迭代器。可见,标准化的迭代器其实已经被各种集合类实现了,否则用户就无法站在Collection接口的抽象高度上对任何集合进行统一遍历。

三、手写迭代器

汽车前挡风玻璃上安装了一台行车记录仪,它最主要的一项功能就是记录行驶路途中所拍摄的视频信息,以防发生交通事故后作为证据之用。我们知道,行车记录仪所记录下来的视频文件是比较大的,同时其存储空间又是有限的,那么它是怎样确保一直不间断地录制视频,并且存储空间不被占满呢?这就需要我们深究其内部数据结构了。

其实,行车记录仪的视频录制存盘操作有循环覆写的特性,待空间不够用时,新录的视频就会覆盖最早的视频,以首尾相接的环形结构来解决存储空间有限的问题。

1.定义迭代器

public interface Iterator<E> 
    //返回下一个元素
    E next();
    //是否还有下一个元素
    boolean hasNext();


2.行车记录仪类

public class DrivingRecorder

    private int index = -1;//当前记录的位置
    private String[] records = new String[10]; //假设只能记录10条视频

    public void append(String record)
        //索引重置,从头覆盖
        if(index == 9 )
            index = 0;
        else 
            index++;
        
        records[index] = record;
    


    public Iterator<String> iterator() 
        return new Itr();
    

    private class Itr implements Iterator<String>
        int cursor = index;//迭代器游标,不破及原始集合索引
        int loopCount = 0;

        @Override
        public String next() 
            int i = cursor;
            if(cursor == 0)
                cursor = 9;
            else 
                cursor--;
            
            loopCount++;
            return  records[i];
        

        @Override
        public boolean hasNext() 
            return loopCount < 10;
        
    

3.客户端类

public class Client 
    public static void main(String[] args) 
        DrivingRecorder dr = new DrivingRecorder();
        for (int i = 0; i < 12; i++) 
            dr.append("视频_" + i);
        
        List<String> uStorage = new ArrayList<>();
        Iterator it = dr.iterator();
        while (it.hasNext())
            String video = (String) it.next();
            System.out.println(video);
            if("视频_10".equals(video) || "视频_8".equals(video))
                uStorage.add(video);
            
        
        System.out.println("事故证据" + uStorage);
    


输出结果:
视频_11
视频_10
视频_9
视频_8
视频_7
视频_6
视频_5
视频_4
视频_3
视频_2
事故证据[视频_10, 视频_8]

说明:

  1. 至此,我们实现的迭代器已经基本完成,用户不但可以使用Iterator进行迭代,而且foreach循环也得到了支持,用户再也不必为捉摸不定的迭代方式而犯愁了。当然,为保持简单,我们并没有实现迭代器的所有功能接口,例如对remove()功能接口的实现,利用这个接口用户便可以删除视频记录了。读者可以在此基础上继续代码实践,需要注意的是对迭代器游标的控制。

总结

提示:这里对文章进行总结:

  1. 为了完成对各种集合类的遍历,我们定义了统一的迭代器接口Iterator,基于此我们让集合以内部类的方式实现其特有的迭代逻辑,再将自己标记为Iterable并返回迭代器实例,以证明自己是具备迭代能力的。具体的集合内部结构与迭代逻辑对于客户端这个“局外人”是透明的,客户端只需要知道这个集合是可以迭代的,并向集合发起迭代请求以获取迭代器即可以进行标准方式的遍历了。

  2. 迭代器模式的各角色定义如下:

  • ConcreteAggregate(集合实现):实现集合接口Aggregate的具体集合类,可以实例化并返回一个迭代器以供外部使用。对应本章例程中的行车记录仪类DrivingRecorder。
  • Iterator(迭代器接口):迭代器的接口标准,定义了进行迭代操作所需的一些方法,如next()、hasNext()等。
  • ConcreteIterator(迭代器实现):迭代器接口Iterator的具体实现类,记录迭代状态并对外部提供所有迭代器功能的实现。
  • Client(客户端):集合数据的使用者,需要从集合获取迭代器再进行遍历。

以上是关于行为篇-迭代器模式的主要内容,如果未能解决你的问题,请参考以下文章

从零开始学习Java设计模式 | 行为型模式篇:迭代器模式

迭代器模式以及对内部类的运用

Java进阶篇设计模式之九----- 解释器模式和迭代器模式

设计模式 行为型模式 实例 -- 迭代器模式实例:迭代数组

手撸golang 行为型设计模式 迭代器模式

行为型模式-迭代器模式