1、Iterator模式

迭代器(iterator)有时又称游标(cursor)是程序设计的软件设计模式,可在容器(container,例如链表或者阵列)上遍访的接口,设计人员无需关心容器的内容。

Iterator模式 - 一个一个遍历,我们将学习从含有多个元素的集合中将各个元素逐一取出来的iterator模式。

导学

对于数组我们使用的是下标来进行处理的:

1 int array[] = new int[3];    
2 for (int i = 0; i < array.length; i++) {
3     System.out.println(array[i]);
4 }

对ArrayList的处理

1 List<String> list = new ArrayList<String>();
2       for(int i = 0 ; i < list.size() ; i++){
3           String string = list.get(i);
4 }

将这里的i的作用抽象化,通用化后形成的模式,在设计模式中称为iterator模式

Iterator模式用于在数据集合按照顺序遍历集合。英语单词iterate有反复做某件事情的意思,汉语称为“迭代器”。

2、实例程序

这段程序的作用是将书(Book)放置到书架(BookShelf)中,并将书的名字按照顺序显示出来.

类和接口的意义:

Aggregate: 表示集合的接口
Iterator: 遍历集合的接口
BookShelf: 表示书架的类
BookShelfIterator: 遍历书架的类
Book: 表示书的类

2.1 Aggregate接口

package cn.design.iterator;

/**
* @author lin
* @version 1.0
* @date 2020-07-13 14:16
* @Description 表示集合的接口
*/
public interface Aggregate {
   /**
    * 在Aggregate接口中声明的方法只有一个一-iterator 方法。该方法会生成-一个用于遍历集合的迭代器。
    * 想要遍历集合中的元素时,可以调用iterator方法来生成一一个实现了Iterator接口的类的实例。
    */
   public abstract Iterator iterator();
}

2.2 Iterator接口

package cn.design.iterator;

/**
* @author lin
* @version 1.0
* @date 2020-07-13 14:30
* @Description TODO
*/
public interface Iterator {    /**
    * 判断是否存在下一-个元素
    * 当集合中存在下一个元素
    * 时,该方法返回true;当集合中不存在下一个元素,即已经遍历至集合末尾时,该方法返回
    * false。hasNext 方法主要用于循环终止条件。
    *
    * @return
    */
   public abstract boolean hasNext();    /**
    * 取下一个元素
    * 该方法返回的是集合
    * 中的一一个元素。但是,next方法的作用并非仅仅如此。为了能够在下次调用next方法时正确地返
    * 回下一个元素,该方法中还隐含着将迭代器移动至下一个元素的处理。说“隐含”,是因为
    * Iterator接口只知道方法名。想要知道next方法中到底进行了什么样的处理,还需要看一下实
    * 现了Iterator接口的类( BookShelfIterator)。这样,我们才能看懂next方法的作用。
    *
    * @return object
    */
   public abstract Object next();
}

2.3 Book类

package cn.design.iterator;

/**
* @author lin
* @version 1.0
* @date 2020-07-13 14:33
* @Description TODO
*/
public class Book {
   private String name;    public Book(String name) {
       this.name = name;
  }    public Book() {
  }    public void setName(String name) {
       this.name = name;
  }    public String getName() {
       return name;
  }    @Override
   public String toString() {
       return "Book{" +
               "name='" + name + '\'' +
               '}';
  }
}

2.4 BookShelf类

package cn.design.iterator;

/**
* @author lin
* @version 1.0
* @date 2020-07-13 19:00
* @Description TODO
*/
public class BookShelf implements Aggregate {
   /**
    * 这个书架中定义了books字段,它是Book类型的数组。该数组的大小( maxsize )在生成
    * BookShelf的实例时就被指定了。之所以将books字段的可见性设置为private,是为了防止.
    * 外部不小心改变了该字段的值。
    */
   private Book[] books;
   private int last = 0;    public BookShelf(int maxSize) {
       this.books = new Book[maxSize];
  }    public Book getBookAt(int index) {
       return books[index];
  }    public void appendBook(Book book) {
       this.books[last] = book;
       last++;
  }    public int getLength() {
       return last;
  }    /**
    * BookShelf类对应的Iterator。当外部想要遍历书架时,就会调用这个方法。
    *
    * @return Iterator 实现类
    */
   @Override
   public Iterator iterator() {
       return new BookShelfIterator(this);
  }
}

2.5 BookShelfIterator 类

package cn.design.iterator;

/**
* @author lin
* @version 1.0
* @date 2020-07-13 19:03
* @Description TODO
*/
public class BookShelfIterator implements Iterator {
   /**
    * bookShelf字段表示BookShelfIterator所要遍历的书架。
    */
   private BookShelf bookShelf;
   /**
    * index字段表示迭代器当前所指向的书的下标。
    */
   private int index;    /**
    * @param bookShelf
    */    /**
    * 构造函数会将接收到的BookShelf的实例保存在bookShelf字段中,并将index初始化为0。
    *
    * @param bookShelf
    */
   public BookShelfIterator(BookShelf bookShelf) {
       this.bookShelf = bookShelf;
       this.index = 0;
  }    /**
    * hasNext方法是Iterator接口中所声明的方法。该方法将会判断书架中还有没有下一-本书, .
    * 如果有就返回true,如果没有就返回false。而要知道书架中有没有下一本书,可以通过比较
    * index和书架中书的总册数( bookShelf . getLength ()的返回值)来判断。
    *
    * @return
    */
   @Override
   public boolean hasNext() {
       return index < bookShelf.getLength();
  }    /**
    * next方法会返回迭代器当前所指向的书( Book的实例),并让迭代器指向下一-本书。它也是
    * Iterator接口中所声明的方法。next方法稍微有些复杂,它首先取出book变量作为返回值,
    * 然后让index指向后面- -本书。
    * 如果与本章开头的for语句来对比,这里的“让index指向后面一-本书”的处理相当于其中
    * 的i++,它让循环变量指向下一个元素。
    *
    * @return
    */
   @Override
   public Object next() {
       Book bookAt = bookShelf.getBookAt(index);
       index++;
       return bookAt;
  }
}

2.6 TestMain测试类

package cn.design.iterator;

import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.List; /**
* @author lin
* @version 1.0
* @date 2020-07-13 19:05
* @Description TODO
*/
public class TestMain {    public static void main(String[] args) {
       BookShelf bookShelf = new BookShelf(4);
       bookShelf.appendBook(new Book("环游世界80天"));
       bookShelf.appendBook(new Book("圣经"));
       bookShelf.appendBook(new Book("灰姑娘"));
       bookShelf.appendBook(new Book("阿拉丁神灯"));
       Iterator it = bookShelf.iterator();
       while (it.hasNext()) {
           Book book = (Book) it.next();
           System.out.println(book.toString());
      } //       ArrayList<String> list = new ArrayList<>();
//       list.add("aaaa");
//       list.add("bbbb");
//       list.add("cccc");
//       list.add("dddd");
//       java.util.Iterator<String> it2 = list.iterator();
//       while (it2.hasNext()) {
//           System.out.println("it2.next() = " + it2.next());
//       }
  }
}

运行结果如下:

Book{name='环游世界80天'}
Book{name='圣经'}
Book{name='灰姑娘'}
Book{name='阿拉丁神灯'}

通过bookShelf. iterator()得到的it是用于遍历书架的Iterator实例。while部分的条件当然就是it.hasNext()了。只要书架上有书,while 循环就不会停止。然后,程序会通过it.next()一本一本地遍历书架中的书。

3、Iterator模式中的各个角色

读完示例程序,让我们来看看Iterator模式中的登场角色。

1、Iterator (迭代器)

该角色负责定义按顺序逐个遍历元素的接口( API)。在示例程序中,由Iterator接口扮演这个角色,它定义了hasNext和next两个方法。其中,hasNext 方法用于判断是否存在下一个元素,next方法则用于获取该元素。

2、Concretelterator (具体的迭代器)

该角色负责实现Iterator角色所定义的接口( API)。在示例程序中,由BookShelfIterator类扮演这个角色。该角色中包含了遍历集合所必需的信息。在示例程序中,BookShelf类的实例保存在bookShelf字段中,被指向的书的下标保存在index字段中。

3、Aggregate (集合)

该角色负责定义创建Iterator角色的接口( API)。这个接口( API)是-一个方法,会创建出“按顺序访问保存在我内部元素的人”。在示例程序中,由Aggregate接口扮演这个角色,它里面定义了iterator 方法。

4、ConcreteAggregate ( 具体的集合)

该角色负责实现Aggregate角色所定义的接口(API)。它会创建出具体的Iterator角色,即Concretelterator角色。在示例程序中,由BookShelf类扮演这个角色,它实现了iterator 方法。

4、扩展思路的要点

4.1、为何iterator必不可少?

       while (it.hasNext()) {
           Book book = (Book) it.next();
           System.out.println(book.toString());
      }

上述只使用了Iterator的hasNext方法和next方法,并没有调用BookShelf的方法。也就是说,这里的while循环并不依赖于BookShelf的实现。

如果编写BookShelf的开发人员决定放弃用数组来管理书本,而是用java.util. vector取而代之,会怎样呢?不管BookShelf如何变化,只要BookShelf的iterator方法能正确地返回Iterator的实例(也就是说,返回的Iterator类的实例没有问题,hasNext 和next方法都可以正常工作),即使不对上面的while循环做任何修改,代码都可以正常工作。

这对于BookShelf的调用者来说真是太方便了。设计模式的作用就是帮助我们编写可复用的类。所谓“可复用”,就是指将类实现为“组件”,当一个组件发生改变时,不需要对其他的组件进行修改或是只需要很小的修改即可应对。

这样也就能理解为什么在示例程序中iterator方法的返回值不是BookShelfIterator类型而是Iterator类型了(代码清单1-6)。这表明,这段程序就是要使用Iterator的方法进行编程,而不是BookShelfIterator的方法。

4.2、难以理解的抽象类和接口

难以理解抽象类和接口的人常常使用ConcreteAggregate角色和ConcreteIterator角色编程,而不使用Aggregate接口和Iterator接口,他们总想用具体的类来解决所有的问题。

但是如果只使用具体的类来解决问题,很容易导致类之间的强耦合,这些类也难以作为组件被再次利用。为了弱化类之间的耦合,进而使得类更加容易作为组件被再次利用,我们需要引入抽象类和接口。

4.3、Java中ArrayList源码

私有的内部类Itr

/**
    * An optimized version of AbstractList.Itr
    */
   private class Itr implements Iterator<E> {
       int cursor;       // index of next element to return
       int lastRet = -1; // index of last element returned; -1 if no such
       int expectedModCount = modCount;        public boolean hasNext() {
           return cursor != size;
      }        @SuppressWarnings("unchecked")
       public E next() {
           checkForComodification();
           int i = cursor;
           if (i >= size)
               throw new NoSuchElementException();
           Object[] elementData = ArrayList.this.elementData;
           if (i >= elementData.length)
               throw new ConcurrentModificationException();
           cursor = i + 1;
           return (E) elementData[lastRet = i];
      }        public void remove() {
           if (lastRet < 0)
               throw new IllegalStateException();
           checkForComodification();            try {
               ArrayList.this.remove(lastRet);
               cursor = lastRet;
               lastRet = -1;
               expectedModCount = modCount;
          } catch (IndexOutOfBoundsException ex) {
               throw new ConcurrentModificationException();
          }
      }        @Override
       @SuppressWarnings("unchecked")
       public void forEachRemaining(Consumer<? super E> consumer) {
           Objects.requireNonNull(consumer);
           final int size = ArrayList.this.size;
           int i = cursor;
           if (i >= size) {
               return;
          }
           final Object[] elementData = ArrayList.this.elementData;
           if (i >= elementData.length) {
               throw new ConcurrentModificationException();
          }
           while (i != size && modCount == expectedModCount) {
               consumer.accept((E) elementData[i++]);
          }
           // update once at end of iteration to reduce heap write traffic
           cursor = i;
           lastRet = i - 1;
           checkForComodification();
      }        final void checkForComodification() {
           if (modCount != expectedModCount)
               throw new ConcurrentModificationException();
      }
  }

ArrayList内部:

   /**
    * Returns an iterator over the elements in this list in proper sequence.
    *
    * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
    *
    * @return an iterator over the elements in this list in proper sequence
    */
   public Iterator<E> iterator() {
       return new Itr();
  }

获取到Iterator,就可以使用boolean hasNext();和E next();俩个方法进行遍访操作。

Java源码:

发哥讲上传到码云上:

https://gitee.com/naimaohome/talk_about_fage.git

发哥讲

如果你觉得文章还不错,就请点击右上角选择发送给朋友或者转发到朋友圈~

● 扫码关注公众号

1、迭代器 Iterator模式 一个一个遍历 行为型设计模式的更多相关文章

  1. Java 实现迭代器(Iterator)模式

    类图 /** * 自己定义集合接口, 相似java.util.Collection * 用于数据存储 * @author stone * */ public interface ICollection ...

  2. 策略模式 Strategy 政策Policy 行为型 设计模式(二十五)

    策略模式 Strategy   与策略相关的常见词汇有:营销策略.折扣策略.教学策略.记忆策略.学习策略.... “策略”意味着分情况讨论,而不是一概而论 面对不同年龄段的人,面对不同的商品,必然将会 ...

  3. 中介者模式 调停者 Mediator 行为型 设计模式(二十一)

      中介者模式(Mediator)   调度.调停   意图 用一个中介对象(中介者)来封装一系列的对象交互,中介者使各对象不需要显式地相互引用,从而使其耦合松散 而且可以独立地改变它们之间的交互. ...

  4. 设计模式—迭代器Iterator模式

    什么是迭代器模式? 让用户通过特定的接口访问容器的数据,不需要了解容器内部的数据结构. 首先我们先模仿集合中ArrayList和LinkedList的实现.一个是基于数组的实现.一个是基于链表的实现, ...

  5. 设计模式C++描述----20.迭代器(Iterator)模式

    一. 举例说明 我们知道,在 STL 里提供 Iterator 来遍历 Vector 或者 List 数据结构. Iterator 模式也正是用来解决对一个聚合对象的遍历问题,将对聚合的遍历封装到一个 ...

  6. 设计模式——迭代器(Iterator)模式

    概述 迭代器模式简单的说(按我目前的理解)就是一个类提供一个对外迭代的接口,方面调用者迭代.这个迭代接口至少包括两个方法:hasNext()--用于判断是否还有下一个,next()--用于取出下一个对 ...

  7. 迭代器Iterator、for循环遍历、泛型

    java.util.Collection接口 是集合的最顶层的接口,定义了集合共性的方法 接口无法直接创建对象,使用多态的方式创建对象 Collection<集合中的数据类型(泛型)> c ...

  8. 观察者模式 Observer 发布订阅模式 源 监听 行为型 设计模式(二十三)

    观察者模式 Observer 意图 定义对象一种一对多的依赖关系,当一个对象的状态发生改变时,所有依赖他的对象都得到通知并自动更新. 别名:依赖(Dependents),发布订阅(Publish-Su ...

  9. Head First 设计模式 —— 10. 迭代器 (Iterator) 模式

    思考题 public void printMenu() { PancakeHouseMenu pancakeHouseMenu = new PancakeHouseMenu(); ArrayList ...

随机推荐

  1. CMDB01 /paramiko模块、项目概述、项目架构、项目实现

    CMDB01 /paramiko模块.项目概述.项目架构.项目实现 目录 CMDB01 /paramiko模块.项目概述.项目架构.项目实现 1. paramiko 2. 基于xshell连接服务器 ...

  2. A Broken Calculator 最详细的解题报告

    题目来源:A Broken Calculator 题目如下(链接有可能无法访问): A Broken Calculator Time limit : 2sec / Stack limit : 256M ...

  3. Video 自动播放

    先说ios ios之前的政策是视频只能在用户主动操作后才能播放,且播放时必须全屏. 随着 iOS 10 的正式发布,Safari 也迎来了大量更新,首先划出重点:1)iOS 10 Safari 支持特 ...

  4. js常见删除绑定的事件

    1. elem.onclick = null / false;  //直接解除 例子如下: var div = document.getElemetById('id'); div.onclick = ...

  5. SpringCloud或SpringBoot+Mybatis-Plus利用AOP+mybatis插件实现数据操作记录及更新对比

    引文 本文主要介绍如何使用Spring AOP + mybatis插件实现拦截数据库操作并根据不同需求进行数据对比分析,主要适用于系统中需要对数据操作进行记录.在更新数据时准确记录更新字段 核心:AO ...

  6. [Qt2D绘图]-04绘制文字&&绘制路径

    注:学习自<Qt Creator 快速入门>第三版.   文档中的示例参考 Qt Example推荐:Painter Paths Example和Vector Deformation   ...

  7. patelinux 安装

    参考文档:https://china.xilinx.com/support/documentation/sw_manuals/xilinx2017_2/ug1144-petalinux-tools-r ...

  8. 题解 SP3734 【PERIODNI - Periodni】

    考虑用\(DP\)和组合数学来解决. 因为原图像不规则的形状不好处理,所以先用笛卡尔树(性质为小根堆)将其划分成一个一个的矩形. 发现在笛卡尔树上的每个节点都对应一个矩形,矩形高为\(h_x-h_{f ...

  9. 题解 洛谷 P4177 【[CEOI2008]order】

    进行分析后,发现最大收益可以转化为最小代价,那么我们就可以考虑用最小割来解决这道题. 先算出总收益\(sum\),总收益减去最小代价即为答案. 然后考虑如何建图,如何建立最小割的模型. 发现一个任务最 ...

  10. sql 大小写查询 字符串替换 小写xx 改为大写XX

    --sql 大小写查询 select * from 表 where 字段 collate Chinese_PRC_CS_AS='xx' --替换 小写xx 改为大写XX update 表  set  ...