设计模式-迭代器模式

110 阅读4分钟

迭代器模式

定义

  • 迭代器模式又称为游标模式
  • 它提供一种方法访问一个容器对象中各个元素,而又不需暴露该对象的内部细节本质
    • 什么是容器,能够容纳对象的所有类型的都可以称为容器
      • Collection集合类型
      • Set类型
  • 抽离集合对象迭代行为到迭代器中,提供一致的访问接口

组合模式也是为了提供一致的访问接口,关心整体和个体一致

属于行为型模式

生活中的例子

  • 快递迭代分发
  • 刷脸刷票进展

适用场景

  1. 访问一个集合对象的内容不需要暴露他的内部表示
  2. 为遍历不同的集合结构提供统一的方式

通用结构

image-20210106225330851

角色说明

Iterator抽象迭代器

​ 抽象迭代器负责定义访问和遍历元素的接口,而且基本上是有固定的两个方法

  • next
  • hasNext(判断是否已经访问到底部
public interface Iterator<E> {

    E next();

    boolean hasNext();
}

ConcreteIterator具体迭代器

迭代器的实现,主要用来完成容器元素的遍历

public class ConcreteIterator<E> implements Iterator<E> {
  private List<E> list;
  private int cursor = 0;

  public ConcreteIterator(List<E> list) {
    this.list = list;
  }


  public E next() {
    return this.list.get(this.cursor ++);
  }


  public boolean hasNext() {
    return this.cursor < this.list.size();
  }
}

Aggregate抽象容器

​ 容器角色负责提供创建具体迭代器角色的接口

iterator

public interface IAggregate<E> {
  boolean add(E element);

  boolean remove(E element);

  Iterator<E> iterator();
}

Concrete Aggregate具体容器

​ 具体容器实现容器接口定义的方法,创建出容纳迭代器的对象。

public class ConcreteAggregate<E> implements IAggregate<E> {
  private List<E> list = new ArrayList<E>();

  public boolean add(E element) {
    return this.list.add(element);
  }

  public boolean remove(E element) {
    return this.list.remove(element);
  }

  public E get(int index)
  {
    return this.list.get(index);
  }

  public Iterator<E> iterator() {
    return new ConcreteIterator<E>(this.list);
  }

  public int size()
  {
    return list.size();
  }
}

测试

public class Test {
    public static void main(String[] args) {
        //来一个容器对象
        IAggregate<String> aggregate = new ConcreteAggregate<String>();
        //添加元素
        aggregate.add("one");
        aggregate.add("two");
        aggregate.add("three");
        //获取容器对象迭代器
        Iterator<String> iterator = aggregate.iterator();
        //遍历
        while (iterator.hasNext()) {
            String element = iterator.next();
            System.out.println(element);
        }

    }
}

注意:开发系统时,迭代器的删除方法应该完成两个逻辑

  • 一是删除当前元素
  • 二是当前游标指向下一个元素

关于泛型的说明

上面看到了E大家肯定都有疑问

我们对大部分用到的泛型都列一下

  • E
    • Element
    • 代表一个类型,具体是什么类型是在实现或者运行的时候觉得
  • T
    • Type
    • 类的操作类型
  • K
    • Key
  • V
    • Value
  • ?
    • 啥都行

都是一样的,占位,不要做强制=转型

源码中的实现

JDK中的Iterator

public interface Iterator<E> {

  boolean hasNext();


  E next();

  default void remove() {
    throw new UnsupportedOperationException("remove");
  }

  default void forEachRemaining(Consumer<? super E> action) {
    Objects.requireNonNull(action);
    while (hasNext())
      action.accept(next());
  }
}

ArrayList的迭代器

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;

  Itr() {}

  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();
    }
  }

可以看到对应的迭代器实现了HasNext,next,remove方法

FailFast

看到ArrayList的iterator可以看到返回了迭代器Itr

public Iterator<E> iterator() {
  return new Itr();
}

image-20210108234119001

可以看到java.util.ArrayList.Itr#next,当i的长度大于当前ArrayList内部的数组的长度的时候就会抛出ConcurrentModificationException,那么什么情况才会这样呢,当我们在使用迭代器遍历的时候,容器的数据结构变更了,此时我们如果继续遍历很有可能出现问题,所以通过FailFast抛出异常来避免我们使用错误的数据结果

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 class FailFastTest {
  public static void main(String[] args) {
    ArrayList<Integer> lists = initList(10);

    Iterator<Integer> iterator = lists.iterator();
    int count = 0;
    while (iterator.hasNext()) {
      lists.remove(0);
      if (count == 3) {
      }
      System.out.println(iterator.next());
      count++;
    }
  }

  private static ArrayList<Integer> initList(int count) {
    ArrayList<Integer> list = new ArrayList<>();
    for (int i = 1; i < count; i++) {
      list.add(i);
    }
    return list;

  }
}

抛出异常

Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.zzjson.design.pattern.iterator.failfast.FailFastTest.main(FailFastTest.java:27)

HashMap中的迭代器

    final class KeyIterator extends HashIterator
        implements Iterator<K> {
        public final K next() { return nextNode().key; }
    }

    final class ValueIterator extends HashIterator
        implements Iterator<V> {
        public final V next() { return nextNode().value; }
    }

    final class EntryIterator extends HashIterator
        implements Iterator<Map.Entry<K,V>> {
        public final Map.Entry<K,V> next() { return nextNode(); }
    }
abstract class HashIterator {
  public final boolean hasNext() {
    return next != null;
  }

  public final void remove() {
    Node<K,V> p = current;
    if (p == null)
      throw new IllegalStateException();
    if (modCount != expectedModCount)
      throw new ConcurrentModificationException();
    current = null;
    K key = p.key;
    removeNode(hash(key), key, null, false, false);
    expectedModCount = modCount;
  }
}

可以看到提供了三种迭代器都提供了next方法,hasNextremove都是从父类中继承的

  • 可以看到这也是模板方式的使用,设计模式不是固定用什么设计模式,而是取决于什么场景适用什么设计模式

Mybatis DefaultCursor

可以看到Mybatis的内部类也提供了对应的迭代器


private class CursorIterator implements Iterator<T> {


  T object;


  int iteratorIndex = -1;

  @Override
  public boolean hasNext() {
    if (object == null) {
      object = fetchNextUsingRowBound();
    }
    return object != null;
  }

  @Override
  public T next() {
    T next = object;

    if (next == null) {
      next = fetchNextUsingRowBound();
    }

    if (next != null) {
      object = null;
      iteratorIndex++;
      return next;
    }
    throw new NoSuchElementException();
  }

  @Override
  public void remove() {
    throw new UnsupportedOperationException("Cannot remove element from Cursor");
  }
}

优缺点

优点

  • 多态迭代
  • 简化集合对象接口
  • 元素迭代功能多样化
  • 解耦迭代和计划

缺点

数组,有序列表,使用迭代器方式遍历较为繁琐

日出开发中,我们几乎不会自己写迭代器,除非我们需要定制一个自己的数据结构对应的迭代器,否则,开源框架提供给我们的API完全够用 我的笔记仓库地址gitee 快来给我点个Star吧