zoukankan      html  css  js  c++  java
  • 《设计模式之美》

    一、迭代器模式

    迭代器模式用来遍历集合对象。不过,很多编程语言都将迭代器作为一个基础的类库,直接提供出来了。在平时开发中,特别是业务开发,我们直接使用即可,很少会自己去实现一个迭代器。不过,知其然知其所以然,弄懂原理能帮助我们更好的使用这些工具类,所以,我觉得还是有必要学习一下这个模式。ArrayIterator代码示例:

    public class ArrayIterator<E> implements Iterator<E> {
      private int cursor;
      private ArrayList<E> arrayList;
    
      public ArrayIterator(ArrayList<E> arrayList) {
        this.cursor = 0;
        this.arrayList = arrayList;
      }
    
      @Override
      public boolean hasNext() {
        return cursor != arrayList.size(); //注意这里,cursor在指向最后一个元素的时候,hasNext()仍旧返回true。
      }
    
      @Override
      public void next() {
        cursor++;
      }
    
      @Override
      public E currentItem() {
        if (cursor >= arrayList.size()) {
          throw new NoSuchElementException();
        }
        return arrayList.get(cursor);
      }
    }
    
    
    public interface List<E> {
      Iterator iterator();
      //...省略其他接口函数...
    }
    
    public class ArrayList<E> implements List<E> {
      //...
      public Iterator iterator() {
        return new ArrayIterator(this);
      }
      //...省略其他代码
    }
    
    public class Demo {
      public static void main(String[] args) {
        List<String> names = new ArrayList<>();
        names.add("xzg");
        names.add("wang");
        names.add("zheng");
        
        Iterator<String> iterator = names.iterator();
        while (iterator.hasNext()) {
          System.out.println(iterator.currentItem());
          iterator.next();
        }
      }
    }

    结合刚刚的例子,我们来总结一下迭代器的设计思路。总结下来就三句话:迭代器中需要定义 hasNext()、currentItem()、next() 三个最基本的方法。待遍历的容器对象通过依赖注入传递到迭代器类中。容器通过 iterator() 方法来创建迭代器。

    二、迭代器模式的优势

    迭代器的原理和代码实现讲完了。接下来,我们来一块看一下,使用迭代器遍历集合的优势。for 循环遍历方式比起迭代器遍历方式,代码看起来更加简洁。那我们为什么还要用迭代器来遍历容器呢?为什么还要给容器设计对应的迭代器呢?

    原因有以下三个。

    首先,对于类似数组和链表这样的数据结构,遍历方式比较简单,直接使用 for 循环来遍历就足够了。但是,对于复杂的数据结构(比如树、图)来说,有各种复杂的遍历方式。比如,树有前中后序、按层遍历,图有深度优先、广度优先遍历等等。如果由客户端代码来实现这些遍历算法,势必增加开发成本,而且容易写错。如果将这部分遍历的逻辑写到容器类中,也会导致容器类代码的复杂性。前面也多次提到,应对复杂性的方法就是拆分。我们可以将遍历操作拆分到迭代器类中。比如,针对图的遍历,我们就可以定义 DFSIterator、BFSIterator 两个迭代器类,让它们分别来实现深度优先遍历和广度优先遍历。

    其次,将游标指向的当前位置等信息,存储在迭代器类中,每个迭代器独享游标信息。这样,我们就可以创建多个不同的迭代器,同时对同一个容器进行遍历而互不影响。

    最后,容器和迭代器都提供了抽象的接口,方便我们在开发的时候,基于接口而非具体的实现编程。当需要切换新的遍历算法的时候,比如,从前往后遍历链表切换成从后往前遍历链表,客户端代码只需要将迭代器类从 LinkedIterator 切换为 ReversedLinkedIterator 即可,其他代码都不需要修改。除此之外,添加新的遍历算法,我们只需要扩展新的迭代器类,也更符合开闭原则。

    还可以参考这个文章:https://blog.csdn.net/gs344937933/article/details/89676475

  • 相关阅读:
    StampedLock
    面试题:final关键字
    VTK 图像处理_显示(vtkImageViewer2 & vtkImageActor)
    VTK 图像处理_创建
    VTK 数据读写_图像数据的读写
    VTK 基本数据结构_如何把几何结构&拓扑结构加入到数据集
    VTK 基本数据结构_数据对象&数据集
    VTK 可视化管道的连接与执行
    VTK 坐标系统及空间变换(窗口-视图分割)
    VTK 三维场景基本要素:相机
  • 原文地址:https://www.cnblogs.com/sunada2005/p/14420745.html
Copyright © 2011-2022 走看看