Java集合框架2——迭代器

来源:互联网 发布:硬笔入门字帖 知乎 编辑:程序博客网 时间:2024/05/20 06:06

1.什么是迭代器(Iterator)

    迭代器是一种设计模式,它提供一种方法访问一个容器对象中的各个元素,而又不用暴露该对象的内部细节。在Java中,因为容器众多,而对容器的操作具有极大的共性,于是Java采用了迭代器为各种容器提供公共的操作接口。简单理解,迭代器就是容器中元素的取出方式。
    迭代器Iterator有三个函数
booleanhasNext() 如果仍有元素可以迭代,则返回 trueEnext() 返回迭代的下一个元素。voidremove() 从迭代器指向的 collection 中移除迭代器返回的最后一个元素(可选操作)。

2.Java中迭代器的使用

package collection;import java.util.ArrayList;import java.util.Iterator;public class IteratorDemo {public static void sop(Object obj) {System.out.println(obj);}public static void main(String[] args) {//创建一个集合ArrayList a1 = new ArrayList();a1.add("java01");a1.add("java02");a1.add("java03");a1.add("java04");//获取一个迭代器Iterator it = a1.iterator();//遍历while(it.hasNext()){sop(it.next());}//上面这样写结构比较清晰,但是开发中一般使用下面这种写法//因为Iterator对象定义在for循环内部,生命周期只在for循环当中//这样可以节约系统的开销 for(Iterator it2 = a1.iterator(); it2.hasNext() ; ){ sop(it2.next()); }}}

3.迭代器使用的注意事项

package collection;import java.util.ArrayList;import java.util.Iterator;public class IteratorDemo2 {public static void sop(Object obj) {System.out.println(obj);}public static void main(String[] args) {//创建一个集合ArrayList a1 = new ArrayList();a1.add("java01");a1.add("java02");a1.add("java03");a1.add("java04");//获取一个迭代器Iterator it = a1.iterator();//遍历while(it.hasNext()){Object obj = it.next();if(obj.equals("java02")){al.add("java008");//注意!!!}}sop(a1);}}
    注意!!!
    如果在迭代器中使用集合的方法(如上面的add方法),则会抛出ConcurrentModificationException异常,该异常的介绍:当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。
    未使用迭代器时,当前各个元素的引用已经存放在了集合当中,对集合使用add方法是没有问题的,但此时使用迭代器之后,元素的引用也给了迭代器,这样就存在两种修改集合的途径,如果此时用集合的方法修改集合,会导致迭代器指向发生异常,这样是非常不安全的,所以迭代器抛出了这个异常。这种特性叫做快速失败特性。
    所以,在迭代时,只能用迭代器的方法操作元素,可是Iterator方法是有限的,只能对元素进行判断,取出,删除的操作,如果想要其他的操作如添加,修改等,就需要使用其子接口,ListIterator。
    P.S 什么叫做快速失败特性?
    从高级别层次来说快速失败是一个系统或软件对于其故障做出的响应。一个快速失败系统设计用来即时报告可能会导致失败的任何故障情况,它通常用来停止正常的操作而不是尝试继续做可能有缺陷的工作。当有问题发生时,快速失败系统即时可见地发错错误告警。在Java中,快速失败与iterators有关。如果一个iterator在集合对象上创建了,其它线程欲“结构化”的修改该集合对象,并发修改异常(ConcurrentModificationException)抛出。

4.迭代器模式

下面来详细研究一下Java中的迭代器。
在Java中与迭代器相关的接口有两个,一个是Iterator,另一个是Iterable.
 
Iterator就是迭代器,这个接口和它的子类与迭代器的具体实现有关。
public interface Iterator<E> {    boolean hasNext();    E next();    void remove();}
Iterable可以理解为可迭代的,那些想要实现迭代器功能的类,就必须实现这个接口。
public interface Iterable<T> {    Iterator<T> iterator();}
 
首先,因为集合想使用迭代器,所以集合的根接口Collection就继承了Iterable这个接口
public interface Collection<E>extends Iterable<E>
然后Collection的子类就得把这个接口实现了,从Collection一直传啊传,直到传到了AbstractCollection类终于把这个接口实现了
public Iterator<E> iterator() {      return new Itr();  }  
那么Itr()又是什么东西呢?它其实AbstractList类中的一个内部类,把迭代器的接口的三个函数给实现了
private class Itr implements Iterator<E> {      /**      * Index of element to be returned by subsequent call to next.      */      int cursor = 0;      /**      * Index of element returned by most recent call to next or      * previous.  Reset to -1 if this element is deleted by a call      * to remove.      */      int lastRet = -1;      /**      * The modCount value that the iterator believes that the backing      * List should have.  If this expectation is violated, the iterator      * has detected concurrent modification.      */      int expectedModCount = modCount;      public boolean hasNext() {          return cursor != size();      }      public E next() {          checkForComodification();          try {              int i = cursor;              E next = get(i);              lastRet = i;              cursor = i + 1;              return next;          } catch (IndexOutOfBoundsException e) {              checkForComodification();              throw new NoSuchElementException();          }      }      public void remove() {          if (lastRet < 0)              throw new IllegalStateException();          checkForComodification();          try {              AbstractList.this.remove(lastRet);              if (lastRet < cursor)                  cursor--;              lastRet = -1;              expectedModCount = modCount;          } catch (IndexOutOfBoundsException e) {              throw new ConcurrentModificationException();          }      }      final void checkForComodification() {          if (modCount != expectedModCount)              throw new ConcurrentModificationException();      }  }  
而我们通常使用的ArrayList则是继承自AbstractList,当我们调用ArrayList的iterator()方法时,它就会返回一个Iterator接口,所以我们使用迭代器之前都得写上一句Iterator it =xx.iterator();在这里,it实质上存的就是Itr,当我们调用it.hasNext()方法时,实质上调用的是Itr.hasNext()。这样我们就把迭代器具体的实现和迭代器的接口联系起来了,如果我们使用的集合不再是LIst类型而是其他类型,比如Set类型的话,只要把Set下面的Itr()方法根据实际需求重写就行了(当然Java本身已经帮你写好了,而且Set中该方法不叫Itr),而接口还是一样,我们调用的时候依然只要写Iterator it = xx.iterator();如此一来,我们遍历集合时就跟不需要知道集合底层的细节。一般不用for、while的角标遍历,也是体现了迭代器的封装特性。
 
在文章最开始的时候说过,迭代器是一种设计模式,现在我们再来看看这个设计模式应该不难理解了,看图
迭代器模式包含四个参与者:
1.容器角色(Aggregate):容器角色负责提供创建具体迭代器角色的接口。
2.具体容器角色(Concrete Aggregate):具体容器角色实现创建具体迭代器角色的接口
3.迭代器角色(Iterator):迭代器角色负责定义访问和遍历元素的接口。
4.具体迭代器角色(Concrete Iterator):具体迭代器角色要实现迭代器接口,并要记录遍历中的当前位置。
 
接下来我们再把刚才的代码一一与上面四个角色对应起来,看看Java是如何体现这个迭代模式的:
1.容器角色
  Collection类提供创建具体迭代器角色的接口
public interface Collection<E> extends Iterable<E> {    //省略其他....    Iterator<E> iterator();}
2.具体容器角色
  这是AbstractList中的一个方法,返回了具体迭代器角色的接口
public Iterator<E> iterator() {        return new Itr();    }
3.迭代器角色
  这是Iterator接口,定义了访问和遍历元素的接口
public interface Iterator<E> {    boolean hasNext();    E next();    void remove();}

4.具体迭代器角色
  这是AbstractList的内部类,具体实现了Iterator接口
private class Itr implements Iterator<E> {    int cursor = 0;    int lastRet = -1;    int expectedModCount = modCount;    public boolean hasNext() {    //代码省略            }    public E next() {    //代码省略            }    public void remove() {    //代码省略            }    final void checkForComodification() {    //代码省略            }}
大概了解了迭代模式之后也许大家会问,为何迭代模式要有一个Iterable,而不直接把Iterator实现了?
        如果Collection直接实现了Iterator接口,那么就必须实现具体实现hasNext()等方法,而我们集合的种类非常多,这样Collection会变得非常复杂,而如果采用Iterable()返回Iterator()的方式,则会变得简单多了,因为迭代器的具体实现方式可以放在子类当中,子类只要能够返回Iterator,它的遍历方法可以自己自由定义。另外,由于Iterator中的hasNext方法实际上是操作指针的,当集合在不同的方法中使用时,迭代的位置变得不可预测,而如果用Iterable()返回Iterator(),那么每次返回的都是new出来的Irerator,里面有一句int cursor = 0;就是把迭代的位置重置了,这样更符合我们的实际需求。
0 0