迭代对于我们搞Java的来说绝对不陌生。我们常常使用JDK提供的迭代接口进行Java集合的迭代。

  Iterator iterator = list.iterator();

  while(iterator.hasNext()){

    String string = iterator.next();

    //do something

  }

  迭代其实我们可以简单地理解为遍历,是一个标准化遍历各类容器里面的所有对象的方法类,它是一个很典型的设计模式。Iterator模式是用于遍历集合类的标准访问方法。它可以把访问逻辑从不同类型的集合类中抽象出来,从而避免向客户端暴露集合的内部结构。 在没有迭代器时我们都是这么进行处理的。如下:

  对于数组我们是使用下标来进行处理的:

  int[] arrays = new int[10];

  for(int i = 0 ; i < arrays.length ; i++){

    int a = arrays[i];

    //do something

  }

  对于ArrayList是这么处理的:

  List<String> list = new ArrayList<String>();

  for(int i = 0 ; i < list.size() ; i++){

    String string = list.get(i);

    //do something

  }

  对于这两种方式,我们总是都事先知道集合的内部结构,访问代码和集合本身是紧密耦合的,无法将访问逻辑从集合类和客户端代码中分离出来。同时每一种集合对应一种遍历方法,客户端代码无法复用。 在实际应用中如果需要将上面两个集合进行整合是相当麻烦的。所以为了解决以上问题,Iterator模式腾空出世,它总是用同一种逻辑来遍历集合。使得客户端自身不需要来维护集合的内部结构,所有的内部状态都由Iterator来维护。客户端从不直接和集合类打交道,它总是控制Iterator,向它发送"向前","向后","取当前元素"的命令,就可以间接遍历整个集合。

  上面只是对Iterator模式进行简单的说明,下面我们看看Java中Iterator接口,看他是如何来进行实现的。

  在Java中Iterator为一个接口,它只提供了迭代的基本规则,在JDK中他是这样定义的:对 collection 进行迭代的迭代器。迭代器取代了 Java Collections Framework 中的 Enumeration。迭代器与枚举有两点不同: 

  1、迭代器允许调用者利用定义良好的语义在迭代期间从迭代器所指向的 collection 移除元素。

  2、方法名称得到了改进。

  其接口定义如下:

  public interface Iterator {  

    boolean hasNext();  

    Object next();  

    void remove();

  }

  

  其中:

  Object next():返回迭代器刚越过的元素的引用,返回值是Object,需要强制转换成自己需要的类型

  boolean hasNext():判断容器内是否还有可供访问的元素

  void remove():删除迭代器刚越过的元素

  对于我们而言,我们只一般只需使用next()、hasNext()两个方法即可完成迭代。如下:

  for(Iterator it = c.iterator(); it.hasNext(); ) {  

    Object o = it.next();  

    //do something

  }

  前面阐述了Iterator有一个很大的优点,就是我们不必知道集合的内部结构,集合的内部结构、状态由Iterator来维持,通过统一的方法hasNext()、next()来判断、获取下一个元素,至于具体的内部实现我们就不用关心了。但是作为一个合格的程序员我们非常有必要来弄清楚Iterator的实现。下面就ArrayList的源码进行分析分析。

  下面就ArrayList的Iterator实现来分析,其实如果我们理解了ArrayList、Hashset、TreeSet的数据结构,内部实现,对于他们是如何实现Iterator也会胸有成竹的。因为ArrayList的内部实现采用数组,所以我们只需要记录相应位置的索引即可,其方法的实现比较简单。

  2.1、ArrayList的Iterator实现

  在ArrayList内部首先是定义一个内部类Itr,该内部类实现Iterator接口,如下:

  

  而ArrayList的iterator()方法实现:

  

  所以通过使用ArrayList.iterator()方法返回的是Itr()内部类,所以现在我们需要关心的就是Itr()内部类的实现:

  在Itr内部定义了三个int型的变量:cursor、lastRet、expectedModCount。其中cursor表示下一个元素的索引位置,lastRet表示上一个元素的索引位置

    int cursor;

    int lastRet = -1;

    int expectedModCount = modCount;

  从cursor、lastRet定义可以看出,lastRet一直比cursor少一所以hasNext()实现方法异常简单,只需要判断cursor和lastRet是否相等即可。

    public boolean hasNext() {

      return cursor != size;

    }

  对于next()实现其实也是比较简单的,只要返回cursor索引位置处的元素即可,然后修改cursor、lastRet即可,

  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;//cursor + 1

    return (E) elementData[lastRet = i];//lastRet + 1 且返回cursor处元素

  }

  checkForComodification()主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过。在ArrayList篇中已经阐述了。modCount用于记录ArrayList集合的修改次数,初始化为0,每当集合被修改一次(结构上面的修改,内部update不算),如add、remove等方法,modCount + 1,所以如果modCount不变,则表示集合内容没有被修改。该机制主要是用于实现ArrayList集合的快速失败机制,在Java的集合中,较大一部分集合是存在快速失败机制的,这里就不多说,后面会讲到。所以要保证在遍历过程中不出错误,我们就应该保证在遍历过程中不会对集合产生结构上的修改(当然remove方法除外),出现了异常错误,我们就应该认真检查程序是否出错而不是catch后不做处理。

  final void checkForComodification() {

    if (modCount != expectedModCount) {

      throw new ConcurrentModificationException();

    }

  }

  对于remove()方法的是实现,它是调用ArrayList本身的remove()方法删除lastRet位置元素,然后修改modCount即可。

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

    }

  }

  这里就对ArrayList的Iterator实现讲解到这里,对于Hashset、TreeSet等集合的Iterator实现,各位如果感兴趣可以继续研究,个人认为在研究这些集合的源码之前,有必要对该集合的数据结构有清晰的认识,这样会达到事半功倍的效果!!!!

 以上内容均来自http://www.cnblogs.com/chenssy/博客,此博客为本人学习笔记

30.Iterator的更多相关文章

  1. Java中Iterator类的详细介绍

    迭代器模式:就是提供一种方法对一个容器对象中的各个元素进行访问,而又不暴露该对象容器的内部细节. 概述 Java集合框架的集合类,我们有时候称之为容器.容器的种类有很多种,比如ArrayList.Li ...

  2. 关于Map集合

    Map接口实现Collection接口,是集合三大接口之一. Map接口在声明:public interface Map<K,V>;将键映射到值的对象,一个映射不能包含重复的键,每个键最多 ...

  3. 【Java学习笔记】泛型

    泛型: jdk1.5出现的安全机制 好处: 1.将运行时期的问题ClassCastException转到了编译时期. 2.避免了强制转换的麻烦. <>: 什么时候用? 当操作的引用数据类型 ...

  4. Java 常用单词

    1.Object-Oriented ['əbdʒekt'ɔ:rɪəntɪd] 面向对象 adj 2.inheritance  [ɪnˈherɪtəns]  继承;遗传;遗产 n inherit  [ɪ ...

  5. Java基础---集合框架---迭代器、ListIterator、Vector中枚举、LinkedList、ArrayList、HashSet、TreeSet、二叉树、Comparator

    为什么出现集合类? 面向对象语言对事物的体现都是以对象的形式,所以为了方便对多个对象的操作,就对对象进行存储,集合就是存储对象最常用的一种方式. 数组和集合类同是容器,有何不同? 数组虽然也可以存储对 ...

  6. 多线程处理慢sql查询小笔记~

    多线程处理慢sql查询以及List(Array)的拆分 系统数据量不大,但是访问速度特别慢,使用多线程优化一下!!! 优化结果:访问时间缩短了十几秒  25s --> 8s 一.List的拆分: ...

  7. 迭代器和增强for

    增强for 内部原理其实是个Iterator迭代器,所以在遍历的过程中,不能对集合中的元素进行增删操作. 格式: for(元素的数据类型  变量 : Collection集合or数组){ } 它用于遍 ...

  8. Java防止SQL注入的几个途径

    java防SQL注入,最简单的办法是杜绝SQL拼接,SQL注入攻击能得逞是因为在原有SQL语句中加入了新的逻辑,如果使用 PreparedStatement来代替Statement来执行SQL语句,其 ...

  9. 深入理解ES6之迭代器与生成器

    迭代器 迭代器 iterator,在 Javascript 中,迭代器是一个对象(也可称作为迭代器对象),它提供了一个 next() 方法,用来返回迭代序列中的下一项. next 方法的定义,next ...

随机推荐

  1. AD16PCB如何快速删除走线

    工具(Tools)取消布线(Un_Route)全部(AII) ad pcb画图,如果想整体去掉一条线,只要是连接在一起的,不管在哪一层,都可以采取如下方法:1.PCB画面下,按组合键Ctrl+H,会出 ...

  2. vue如何使用rules对表单字段进行校验

    基于element-ui 1.在代码中,添加属性::rule <el-form :model="form" :rules="rules" ref=&quo ...

  3. Intellij Idea15 快捷键设置大全

    一:菜单快捷键  快捷键 设置Bar快捷键,参考文章 进入(Enter Full Screen) Alt+S    /(Status Bar) Alt+T    /(Toolbar) Alt+B    ...

  4. 炫酷MD风之dialog各种对话框

    这个demo也是我从别人那里学来的,不是本人写的代码,我也是个MD初学者.把这个demo分享给看到的你,希望对你有帮助. 直接上图: demo地址:百度网盘:链接:https://pan.baidu. ...

  5. java常用集合浅层解析-面试必备

    ArrayList 1.动态数组 2.线程不安全 3.存储空间连续 4.查询快,添加删除慢 构造方法 /** + Shared empty array instance used for defaul ...

  6. VUE(相关简介及初始)

    1.什么是vue Vue.js(读音 /vjuː/, 类似于 view) 是一套构建用户界面的 渐进式框架.与其他重量级框架不同的是,Vue 采用自底向上增量开发的设计.Vue 的核心库只关注视图层, ...

  7. 后台跨域(CORS)

    解决跨域问题 一.为什么会有跨域问题? 是因为浏览器的同源策略是对ajax请求进行阻拦了,但是不是所有的请求都给做跨域,像是一般的href属性,a标签什么的都不拦截. 二.解决跨域问题的两种方式 JS ...

  8. docker--centos镜像安装tomcat jdk1.8 mysql部署java web项目

    一.下载centos7标准镜像及安装mysql5.7 在centos安装mysql5.7 二.安装jdk 1.查询可用jdk版本 yum search java|grep jdk 2.根据搜索到的jd ...

  9. EasyPR源码剖析(4):车牌定位之Sobel算子定位

    一.简介 sobel算子主要是用于获得数字图像的一阶梯度,常见的应用是边缘检测. Ⅰ.水平变化: 将 I 与一个奇数大小的内核进行卷积.比如,当内核大小为3时, 的计算结果为: Ⅱ.垂直变化: 将: ...

  10. Python 判断文件是否存在

    参考:https://www.cnblogs.com/jhao/p/7243043.html