设计模式之迭代器模式

一情景

   在我平时开发的过程中,会设计到很多的集合问题,当然也会涉及很多对这个集合做操作,比如遍历集合,修改元素,移除元素或者增加一个元素,这样做的话就会有两个问题,一我们能直接访问集合的内部结构导致集合数据的安全隐患,二就是我们可能按照一定的顺序来遍历集合,比如从前到后,也可以从后到前,我们不可能直接去修改这个集合本身的遍历方式。所以我们只能用一个辅助的对象来帮我做这些操作。这个辅助的对象不仅能遍历这一个集合也可以遍历另外一个集合,也可以用不同的方式遍历集合,如果用这个辅助对象帮我完成这些操作,用户也不用直接去访问数据结构,导致数据安全问题。根据依赖置换原则我们就把这个辅助的对象抽象化。这种方式就是我学习的迭代器模式,我这里理解的遍历不是for或者while循环。

二迭代器模式

 一个对象不需要考虑是什么但需要遍历的时候,我们可以考虑使用迭代器模式。什么意思呢?举个例子假设我们定义两个集合一个存放动物猫,一个存放飞机,我们需要对这两个集合进行迭代,此时就需要定义一个迭代器,不用管它们是什么东西(不用管是猫还是飞机,这两个东西不属于同一种类)都能使用这个迭代器。

 迭代器模式 提供一种方法顺序访问一个聚合对象中的各个元素,这个模式叫做迭代器模式。


具体实现,第一步需要定义一个抽象的迭代器,用来管理具体的迭代器实现,具体有三个行为,一个是获取下一个元素,一个是获取第一个元素,一个是判断是否还有下一个元素。这里我是简单的抽象一个迭代器,可能还有很多其他行为,比如移除等。

public  interface      Itor  {
public Object next();//获取下一个元素
public Object first();//获取第一个元素
public boolean hasNext();//判断是否还有下一个元素

}

然后应该是具体的实现迭代器的类,这里我先不放,一会再说原因,先放具体需要别被迭代的聚合对象

聚合对象的抽象类

public   abstract class    Agg    {
public abstract Itor creatItor();//创建一个迭代器

}

具体的聚合对象,

public     class    ConAgg  extends Agg  {
    private Object[]   list;
    public ConAgg(Object[] list){
    this.list=list;
    }
public Itor creatItor() {//具体的创建迭代器 1处
return new ConItor();

}

  具体实现迭代器的类 放在内部类里面,节省资源。这里使用的是从前往后的顺序访问目标聚合类

public  class      ConItor  implements Itor{// 2处
 public int count=0;
 
 public ConItor(){
 
 }
public Object next() {//这里使用从前往后的顺序访问,也可以调整自己想要的方式
Object ret=null;
count++;
if(count<list.length){
ret=list[count];
}

return ret;
}


public Object first() {

return list[0];
}
public boolean hasNext() {
return count<list.length-1;
}

}

}

客户端代码

String[] list={"s","a","c","d","a2","wq"};
ConAgg l= new ConAgg(list);
Itor  it= l.creatItor();
while(it.hasNext()){//看到这里是不是有一种很熟悉的感觉,对的我们平常使用的Iterator就是迭代器模式
System.out.println(it.next());

}
 }

       看2处代码 ,我解释一下,刚刚为啥说先不编写迭代器的实现方式的代码,我是准备把这个类放在聚合类的内部类里面了这样有一个好处,如果我把这个类分离出去,在聚合对象1处new  的时候,我就需要把对象ConAgg 中的list集合传到迭代器对象里才行(比如return new ConItor(list);),放在内部类里面就能实现主类和内部类实现资源共享,这样也节省了资源。

其实这种方式在我们java中不能说大部反正就我了解的很多都用这种方式,只是他们的内部类实现方式可能不同,比如Arrlist,看一下arrlist实现迭代器的接口

  private class Itr
    implements Iterator<E>
  {
    int cursor;
    int lastRet = -1;
    int expectedModCount = modCount;
    
    private Itr() {}
    
    public boolean hasNext()
    {
      return cursor != size;
    }
    
    public E next()
    {
      checkForComodification();
      int i = cursor;
      if (i >= size) {
        throw new NoSuchElementException();
      }
      Object[] arrayOfObject = elementData;
      if (i >= arrayOfObject.length) {
        throw new ConcurrentModificationException();
      }
      cursor = (i + 1);
      return (E)arrayOfObject[(lastRet = i)];
    }
    
    public void remove()
    {
      if (lastRet < 0) {
        throw new IllegalStateException();
      }
      checkForComodification();
      try
      {
        remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        expectedModCount = modCount;
      }
      catch (IndexOutOfBoundsException localIndexOutOfBoundsException)
      {
        throw new ConcurrentModificationException();
      }
    }
    
    public void forEachRemaining(Consumer<? super E> paramConsumer)
    {
      Objects.requireNonNull(paramConsumer);
      int i = size;
      int j = cursor;
      if (j >= i) {
        return;
      }
      Object[] arrayOfObject = elementData;
      if (j >= arrayOfObject.length) {
        throw new ConcurrentModificationException();
      }
      while ((j != i) && (modCount == expectedModCount)) {
        paramConsumer.accept(arrayOfObject[(j++)]);
      }
      cursor = j;
      lastRet = (j - 1);
      checkForComodification();
    }
    
    final void checkForComodification()
    {
      if (modCount != expectedModCount) {//当
        throw new ConcurrentModificationException();
      }
    }
  }
       看完源码我又想到了一个学习点。

       既然说到这里了就顺便学习一下快速失败,和安全失败,这段代码设计到一个异常ConcurrentModificationException,在这段代码里有两个属性值一个是expectedModCount一个是modCount,在这个迭代器Itr中  初始化的时候这两个值是相等的,int expectedModCount = modCount;

在遍历的过程的过程中,会检测整两个相等不相等,

  if (modCount != expectedModCount) {//当
        throw new ConcurrentModificationException();

      }

如下不不相等就会抛出异常,是什么时候会导致modCount改变呢?就是在遍历的过程中,如果对象本身去改变的话会导致modCount改变,如果使用迭代器来改变的话,就不会改变。这句话什么意思,再看上面的代码,上面的代码是一个具体迭代器实现类,也就是Arrlist里面itr这个类,里面有add和remove 方法,但这些方法中都设置expectedModCount = modCount,所以不会改变,什么叫做使用对象本身更改呢,再看看arrlist对象本身的方法。

  private void ensureExplicitCapacity(int paramInt)
  {
    modCount += 1;//每次改变modCount都会加一
    if (paramInt - elementData.length > 0) {
      grow(paramInt);
    }

  }

这个是ArrayList  本身的add方法,add会调用addensureExplicitCapacity方法,每次改变modCount都会加一,所以在遍历的时候回抛出异常。这里主要针对的是遍历这种方式,不是使用for或者while的方式。

java.util下面的集合都是快速失败的

了解了快速失败,安全绳失败就很好理解,就是如果一个集合不收修改次数影响就是安全失败,java.util.concurrent下的集合都是安全失败的





评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值