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. C# - 设计- Struct与Class的选择

    选择Struct的原则 该类型的实例较小且通常为短生存期,或者通常嵌入到其他对象中. 它以逻辑方式表示单个值,类似于基元类型( int .等 double ). 它的实例大小为16字节. 它是不可变的 ...

  2. 渐进式Web应用(PWA)

    什么是渐进式Web应用? 渐进式Web应用是一种全新的Web技术,让Web应用和原生APP的体验相近或一致. 渐进式Web应用它可以横跨Web技术及Native APP开发的解决方案,对于开发者的优势 ...

  3. bzoj2157旅游

    bzoj2157旅游 题意: 给定有权树,支持单边权修改,路径边权取相反数,路径边权求和,路径边权求最大最小值. 题解: 用link-cut tree link-cut tree与树链剖分有些类似,都 ...

  4. scrapy shell 遇到的问题

    有时候用scrapy shell来调试很方便,但是有些网站有防爬虫机制,所以使用scrapy shell会返回403,比如下面 有两种解决方法: (1):第一种方法是在命令上加上-s USER_AGE ...

  5. day4 python 运算符

    python运算符 1.算数运算符( + - * / // % ** ) # + - * / // % ** # 加 减 乘 除 整除 余数 幂 ​ #注意 #1. / 得到浮点型, // 得看被除数 ...

  6. P5836 [USACO19DEC]Milk Visits S 从并查集到LCA(最近公共祖先) Tarjan算法 (初级)

    为什么以它为例,因为这个最水,LCA唯一黄题. 首先做两道并查集的练习(估计已经忘光了).简单来说并查集就是认爸爸找爸爸的算法.先根据线索理认爸爸,然后查询阶段如果发现他们的爸爸相同,那就是联通一家的 ...

  7. 线上CUP负载过高排查方法

      1.top命令查看线程占据的CPU 注意:上面行的cpu是多个内核的平均CPU,不可能超过100% 下面的cpu是每个进程实际占用的cpu,可能超过100% 备注:查看多个内核cpu,只需要在输入 ...

  8. Mybatis(四)多表操作

    数据库如下: 一.创建数据库所对应的bean类 public class User { private Integer uId; private String username; private St ...

  9. SAS X option

    1. SAS X选项就是调用DOS命令. 例子: option noxwait;/*黑窗口执行完命令后自动关闭*/ %let path =.; %let filter=*.lst; X “ dir & ...

  10. 关于docker--详解安装,常规操作,导入导出等(2017-3-29)

    测试环境 :CentOS 7.1 64位 目的:展示docker的常规使用(安装,常规操作,导入导出等) 其他:关于原理等请参考文章后面的延伸阅读,本文不做深入探讨,且方法不唯一 0x01 关于安装d ...