美文网首页Java高开发
关于Iterator探究和思考

关于Iterator探究和思考

作者: java高并发 | 来源:发表于2019-03-15 16:48 被阅读39次

    前两天,一哥们拿着同一个问题连续找了我两次。一开始以为没什么说的东西,后来越研究越觉得有意思,今天闲来无事,写出来跟大家分享。

    问题是这样的:“迭代集合时,Iterator it=c.iterator() 返回的到底接口Iterator的哪个实现类?”。刚开始我随口就是“查查API不就知道了么”,后来证明查API还真就“不知道”。API显示Iterator只有三个实现类(BeanContextSupport.BCSIterator, EventReaderDelegate, Scanner),但是哪一个都不像是跟迭代有关的。后来查源码,发现Iterator设计和实现的精妙之处。
    

    jdk源码 Iterator接口定义如下:

     public interface Iterator<E> {
         boolean hasNext();//判断容器内是否还有可供访问的元素
         E next();//返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型
         void remove();//删除迭代器刚越过的元素
     }
    

    我们都知道,其实在编程过程中经常使用的,也只有hasNext()和next()。一般我们都是这么进行迭代:

    Iterator it=c.iterator();
    while(it.hasNext()){
      Object o=it.next();
      //do something
    }
    

    不难发现,无论迭代的是List集合还是Set集合,也无论集合底层是采用数组实现的ArrayList、Vector、HashSet,或者是采用链表实现的LinkedList、又或者是采用二叉树实现的TreeSet,统统都是通过统一的方法hasNext()、next()来判断、获取下一个元素,但是具体的内部操作肯定是不一样的,那Iterator是怎么做到的呢?其实,并不是Iterator怎么做到,而是每一个集合类自己分别来进行实现的。下面我以ArrayList为例,跟大家一起分析一下jdk的精妙实现:

    众所周知,ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引就可以了,其方法的实现也是比较简单的。它通过定义内部类内部类,来实现Iterator接口来实现的,如下:
    
    
    private class Itr implements Iterator<E> {
     int cursor = 0;//cursor从0开始,表示下一个元素的索引位置
     int lastRet = -1;//lastRet从-1开始,表示上一个元素的索引位置
     int expectedModCount = modCount;//记录对集合修改的次数,主要是用于实现ArrayList集合的快速失败机制
     /**
      *hasNext()只需判断当前位置是不是处在最后一个了即可.
      */
     public boolean hasNext() {
                return cursor != size();
     }
     /**
      * next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可.
      */
     public E next() {
                checkForComodification();//主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过.
         try {
       E next = get(cursor);//从底层实现数组里取得当前元素
       lastRet = cursor++;//lastRet+1
       return next;//返回当前元素
         } catch (IndexOutOfBoundsException e) {
       checkForComodification();
       throw new NoSuchElementException();
         }
     }
     /**
      * 调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可.
      */
     public void remove() {
         if (lastRet == -1)
          throw new IllegalStateException();
                checkForComodification();
         try {
       AbstractList.this.remove(lastRet);//从底层实现数组里删除上一个元素
       if (lastRet < cursor)
           cursor--;//由于当前元素要填补到上一个元素的位置去,所以当前元素下标-1
       lastRet = -1;
       expectedModCount = modCount;//把集合的修改次数赋值给expectedModCount
         } catch (IndexOutOfBoundsException e) {
          throw new ConcurrentModificationException();
         }
     }
    
     final void checkForComodification() {
         if (modCount != expectedModCount)
      throw new ConcurrentModificationException();
     }
      }
    
    

    这就是ArrayList的Iterator接口的实现。有人会问“那岂不是每一个集合类都要提供对Iterator的实现啊?”,对!Iterator只提供方法,你要想使用其进行迭代遍历,就必须提供对它的迭代实现。实际上,LinkedList、Vector、HashSet、TreeSet等集合的Iterator实现也都采用类似的设计思路。

    通过以上探究,我们不难看出,Iterator给我们提供了一种通用的遍历各种集合的方法,它可以把访问逻辑从不同类型的集合类中抽象出来,做到了访问代码和集合本身的解耦,从而避免向客户端暴露集合的内部结构。从此,客户端可以不直接和集合类打交道,它只需要控制Iterator,向它发送“向前”、“向后”、“取当前元素”、“删元素”的命令,就可以间接遍历和操作整个集合,并且这些客户端代码还是可以复用的。其实,这就是java中非常著名的Iterator设计模式。
    
    书到用时方恨少,事非经过不知难。点滴积累,你就会成为技术上的巨人!
    

    欢迎工作一到五年的Java工程师朋友们加入Java架构开发: 957734884,群内提供免费的Java架构学习资料(里面有高可用、高并发、高性能及分布式、Jvm性能调优、Spring源码,MyBatis,Netty,Redis,Kafka,Mysql,Zookeeper,Tomcat,Docker,Dubbo,Nginx等多个知识点的架构资料)合理利用自己每一分每一秒的时间来学习提升自己,不要再用"没有时间“来掩饰自己思想上的懒惰!趁年轻,使劲拼,给未来的自己一个交代!

    相关文章

      网友评论

        本文标题:关于Iterator探究和思考

        本文链接:https://www.haomeiwen.com/subject/lqjrmqtx.html