我们常常使用 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.util.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 的源码进行分析分析。

二、各个集合的 Iterator 的实现

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

2.1、ArrayList 的 Iterator 实现

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

 private class Itr implements Iterator<E> {
//do something
}

而 ArrayList 的 iterator() 方法实现:

 public Iterator<E> iterator() {
return new Itr();
}

所以通过使用 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() 主要用来判断集合的修改次数是否合法,即用来判断遍历过程中集合是否被修改过。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();
}
}

java集合-Iterator迭代的更多相关文章

  1. JAVA集合--Iterator接口

        本文首发于cartoon的博客     转载请注明出处:https://cartoonyu.github.io/cartoon-blog     上一篇文章中我在集合元素的遍历中已经有涉及到I ...

  2. Java 集合的迭代方式

    集合的迭代流使得程序员得以站在更高的抽象层次上对集合进行操作.传统的迭代方法直接看代码: List<Dog> dogs = new ArrayList<>();        ...

  3. Java集合--Iterator和Enumeration比较

    转载请注明出处:http://www.cnblogs.com/skywang12345/admin/EditPosts.aspx?postid=3311275 第1部分 Iterator和Enumer ...

  4. Java集合Iterator迭代器的实现

    一.迭代器概述 1.什么是迭代器? 在Java中,有很多的数据容器,对于这些的操作有很多的共性.Java采用了迭代器来为各种容器提供了公共的操作接口.这样使得对容器的遍历操作与其具体的底层实现相隔离, ...

  5. Java集合详解3:Iterator,fail-fast机制与比较器

    Java集合详解3:Iterator,fail-fast机制与比较器 今天我们来探索一下LIterator,fail-fast机制与比较器的源码. 具体代码在我的GitHub中可以找到 https:/ ...

  6. Java集合详解3:一文读懂Iterator,fail-fast机制与比较器

    <Java集合详解系列>是我在完成夯实Java基础篇的系列博客后准备开始写的新系列. 这些文章将整理到我在GitHub上的<Java面试指南>仓库,更多精彩内容请到我的仓库里查 ...

  7. Iterator——迭代接口

    迭代对于JAVA的来说绝对不陌生.我们常常使用JDK提供的迭代接口进行Java集合的迭代. Iterator iterator = list.iterator(); while(iterator.ha ...

  8. JAVA集合迭代遍历和特性介绍

    数组.集合:都是一种容器,用一个对象管理多个对象:数组不能自动增长:只能存放同类型的元素 集合能自动扩容:部分集合允许存放不同类型的元素: 1.List: 有顺序的,允许存放重复的元素: 遍历:for ...

  9. java集合 之 Collection和Iterator接口

    Collection是List,Queue和Set接口的父接口,该接口里定义的方法即可用于操作Set集合,也可以用于List和Queue集合.Collection接口里定义了如下操作元素的方法. bo ...

随机推荐

  1. Pycharm远程调试

    1.在pycharm的安装目录中找到pycharm-debug.egg,将其拷贝到目标主机的/usr/lib/python2.7/dist-packages目录下: 执行: sudo easy_ins ...

  2. ThinkPHP5 隐藏接口里面的index.php

    隐藏index.php 官方介绍是这样的:http://www.kancloud.cn/thinkphp/thinkphp5_quickstart/145250 可以去掉URL地址里面的入口文件ind ...

  3. poj 1386 Play on Words(有向图欧拉回路)

    /* 题意:单词拼接,前一个单词的末尾字母和后一个单词的开头字母相同 思路:将一个单词的开头和末尾单词分别做两个点并建一条有向边!然后判断是否存在欧拉回路或者欧拉路 再次强调有向图欧拉路或欧拉回路的判 ...

  4. AIX下如何根据端口号查找相应的进程

    1. $ netstat -Aan |grep 8080 f1000e0002321bb8 tcp 0 0 *.8080 *.* LISTEN 2. $ rmsock f1000e0002321bb8 ...

  5. js让程序暂停一段时间再执行

    function sleep(d){ for(var t = Date.now();Date.now() - t <= d;); } sleep(5000); //当前方法暂停5秒

  6. 同级、父子级div定位

    以两个div右上角对齐为例: 效果图: 1.同级定位 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN&quo ...

  7. [Python] urllib2.HTTPError: HTTP Error 403: Forbidden

    搬运自http://www.2cto.com/kf/201309/242273.html,感谢原作. 之所以出现上面的异常,是因为如果用 urllib.request.urlopen 方式打开一个UR ...

  8. 对于placeholder浏览器兼容性(包括密码输入框)解决办法

    将以下脚本和样式引入你的页面(对于密码输入框,要设置ID属性值): <script type="text/javascript"> $(function () { // ...

  9. pe创建激活administrator后消除问题,删除用户问题

    启动pe进入电脑,打开清楚密码,然后选择administrator,点击激活.然后就可以用administrator登陆电脑. 使用完毕后,想要注销administrator. 方法1. 在cmd中输 ...

  10. [Java 基础]数组

    数组初始化 定义数组语法格式 定义数组有两种方式,如下两种格式是等价的: int[] a1; int a1[];  注:在C/C++中,不支持第一种格式.但是,推荐使用这种方式,因为这样似乎更合理,声 ...