迭代对于我们搞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. Debian 8 安装Nginx最新版本

    在Debian下如果直接apt-get install nginx直接装发现nginx版本是很旧的,本文主要讲一下如何在Debian 8上装新版的nginx. 原文资料:https://nginx.o ...

  2. fuchsia 内核

    1 内核zircon 是c++写的,system call是重写的,不兼容POSIX https://fuchsia.googlesource.com/zircon/+/HEAD/docs/conce ...

  3. hdu 3065 AC自动机 标记数组不清零

    题目链接: http://acm.hdu.edu.cn/showproblem.php?pid=3065 题目里面要我们计算每种单词出现的次数,重叠的也要计算,那么我们在查找的时候不要把标记单词结尾的 ...

  4. 算法练习LeetCode初级算法之设计问题

    打乱数组 不断的让第一个与后面随机选择的数交换 class Solution { private int[] nums; private int[] initnums; public Solution ...

  5. redis缓存与数据库一致性问题

    一般来说,如果允许缓存可以稍微的跟数据库偶尔有不一致的情况,也就是说如果你的系统不是严格要求 “缓存+数据库” 必须保持一致性的话,最好不要做这个方案,即:读请求和写请求串行化,串到一个内存队列里去. ...

  6. 双硬盘,win10安装到固态盘

    1.PE下格式化固态盘的系统盘 2.打开DG分区工具,查看固态盘的系统盘是否为激活状态,红色为激活,如果不是,激活一下 3.用windows安装器,或者hdd安装win10到固态盘 4.bios中启动 ...

  7. 百度地图缩小后marker点的图标移动

    遇到的坑:使用百度地图,当地图缩小时,marker点的自定义图标会出现偏移的问题,marker点的信息窗口会出现闪烁问题. 原因:1.anchor是定位点距离图片左上角的偏移量.如果不给anchor的 ...

  8. mysql 中启动服务的命令 、登录命令、退出命令 mysql 的常用命令

    1.cmd 以管理员执行 下面命令 启动服务 :net start mysql57 关闭 服务:net stop mysql57 查看mysql 的版本信息 : mysql -V 指定主机地址登录: ...

  9. Python3 单下划线_双下划线__开头

    Python 中,下划线对解释器有特殊的含义,而且是内建标识符所使用的符号,使用时要多加留意. 在 Python3 的面向对象中,双下划线开头的变量和方法表名为私有变量和私有方法. __private ...

  10. spring boot 源码分析

    说明:spring boot版本 2.0.6.RELEASE 思绪 首先,大家认识spring boot是从@SpringBootApplication注解和org.springframework.b ...