简介

  迭代器是遍历容器的一种常用方法,它屏蔽了容器的实现细节,无需暴露数据结构内部,就可以对容器进行遍历,迭代器本身也是一种设计模式,迭代是一种特殊的遍历方式。

Iterator

  在java中,迭代器接口Iterator定义了三种方法

public interface Iterator<E> {

  // 是否还有元素可以迭代,如果有 返回true 没有返回false
boolean hasNext(); // 返回迭代的下一个元素
E next(); // 从迭代器指向的 collection 中移除迭代器返回的最后一个元素
void remove();
}

  remove方法是移除迭代器返回的最后一个元素,因此如果没有调用过next方法,直接调用remove方法是错误的做法,并且每执行一次next方法,只能调用一次remove方法

  遍历collection时,

  (1)先调用Collection子类中的iterator()方法创建迭代器对象,

  (2)并用hasNext()方法判断,

  (3)如果仍有元素可迭代,再调用next()方法,返回迭代的下一个元素,依次循环直到最后一个元素。如下:

public class IteratorTest {
public static void main(String[] args) {
// 创建ArrayList<String>对象
ArrayList<String> arrList = new ArrayList<>(); // 添加元素
arrList.add("Iterator");
arrList.add("迭代器");
arrList.add("遍历"); // 用Iterator遍历
Iterator<String> itr = arrList.iterator();//创建迭代器 while(itr.hasNext()){//判断是否有元素可迭代
System.out.println(itr.next());//获取迭代元素
} /* 或者用for循环
    for(Iterator<String> itr1 = arrList.iterator();itr1.hasNext();){
  System.out.println(itr1.next());
    }
*/
}
}

java 中遍历取值异常(Hashtable Enumerator)解决办法

  用迭代器取值时抛出的异常:java.util.NoSuchElementException: Hashtable Enumerator ,比如下面代码:

//使用迭代器遍历
Iterator<String> it = tableProper.stringPropertyNames().iterator();
sqlMap = new HashMap<String,String>();
while(it.hasNext()){
sqlMap.put(it.next(), tableProper.getProperty(it.next()));
}

  这是一个枚举异常,是因为在还没来得及执行it.next()时就开始引用它。我们可以用如下方式解决此问题:

//使用迭代器遍历
Iterator<String> it = tableProper.stringPropertyNames().iterator();
sqlMap = new HashMap<String,String>();
String key;
while(it.hasNext()){
key = it.next();
sqlMap.put(key, tableProper.getProperty(key));
}

  所以一般注意:迭代器循环里面只能有一个 it.next(),否则就会报java.util.NoSuchElementException的错误;

  另外注意,如果迭代器里面只有一条数据,那么it.next()也会报java.util.NoSuchElementException的错误 ,解决办法就是采用增强for each循环(需要事先知道集合内部类型),比如下面项目中遇到的:

if(device.getDeviceInfos()!=null){
for(DeviceInfo h:device.getDeviceInfos()){
device.setSafeLevel(h.getSafeLevel());
device.setLastActiveTime(h.getLastActiveTime());
}
}

  device.getDeviceInfos()为一个set集合,里面只会有一条数据,device.getDeviceInfos().get不到里面safeLevel等,使用iterator因为只有一个,it.next()会报NoSuchElementException错误,所以才有foreach遍历比较好。

Iterable

  Iterable只有一个iterator方法,该方法会返回一个迭代器,一般情况下使用迭代器的类都会实现Iterable接口而非直接实现Iterator接口

public interface Iterable<T> {
// 返回一个在一组 T 类型的元素上进行迭代的迭代器。
Iterator<T> iterator();
}

  foreach 就是对迭代器的另外一种使用方式,只要是实现Iterable接口的类,就可以用foreach对自身进行遍历

map 迭代

  map接口并未继承Iterable接口,因此本身并不能直接使用迭代器,不过可以转化为collection然后进行迭代,例如:

Map<String, String> map = new HashMap<>();
map.put("韦德", "");
Iterator<Entry<String, String>> iterator = map.entrySet().iterator();
while (iterator.hasNext()) {
Entry<String, String> entry = iterator.next();
System.out.println(entry.getKey() + ":" + entry.getValue());
}

  map的遍历方式可参考总结的这篇博客:java中遍历Map几种方法

collection 迭代

  collection接口继承了Iterable接口,因此只要是collection实例,都可以使用迭代器进行遍历

Collection<String> collection = Arrays.asList("java", "c", "js");
Iterator<String> iterator = collection.iterator();
while (iterator.hasNext()) {
String str = iterator.next();
System.out.println(str);
}

  用迭代器遍历collection的优势在于遍历的过程中可以remove元素,用foreach则不行

ListIterator

  ListIterator是对Iterator的扩展,针对定义了ListIterator方法的有序的容器,可以采用ListIterator进行迭代,ListIterator新增了一些方法,可以在遍历时获取数据的索引,或者逆序遍历等。

public interface ListIterator1<E> extends Iterator<E> {

  // 是否还有上一个元素 一般逆序遍历时使用
boolean hasPrevious(); // 返回列表中的上一个元素
E previous(); // 返回对 next 的后续调用所返回元素的索引。(如果列表迭代器在列表的结尾,则返回列表的大小)
int nextIndex(); // 返回对 previous 的后续调用所返回元素的索引。(如果列表迭代器在列表的开始,则返回 -1)
int previousIndex(); // 用指定元素替换 next 或 previous 返回的最后一个元素(可选操作)。只有在最后一次调用 next 或 previous
// 后既没有调用 ListIterator.remove 也没有调用 ListIterator.add 时才可以进行该调用。
void set(E e); // 将指定的元素插入列表(可选操作)。该元素直接插入到 next 返回的下一个元素的前面(如果有)
//或者 previous 返回的下一个元素之后(如果有);
void add(E e); // ... Iterator methods
}

  List提供了两个重载的listIterator方法,可以通过这两个方法拿到一个ListIterator对象

public interface List1<E> extends Collection<E> {

  // 此列表元素的列表迭代器
ListIterator<E> listIterator(); // 此列表元素的列表迭代器 可以指定一个参数index
// index 从列表迭代器返回的第一个元素的索引(通过调用 next 方法)
ListIterator<E> listIterator(int index); // List other methods }

  使用listIterator对List进行逆序遍历

List<String> list = Arrays.asList("java", "c", "js");
ListIterator<String> listIterator = list.listIterator(list.size());
while (listIterator.hasPrevious()) {
System.out.println("前一个元素索引为:" + listIterator.previousIndex());
System.out.println("前一个元素为:" + listIterator.previous());
}

光标(cursor)

  迭代器没有当前元素的概念,但有光标(cursor)的概念,如果不指定光标的位置,光标默认在第一个元素之前,此时如果调用next方法,光标会跳到第一个元素和第二个元素中间,并且返回第一个元素

ListIterator<String> listIterator = list.listIterator(list.size());

  以上代码,指定了光标的位置,在最后一个元素的后面,此次要调用next方法会抛异常,因为没有下一个元素,如果调用nextIndex则会返回容器长度,调用previous则返回最后一个元素,同时光标向上移动一位。

总结

  迭代器在使用的过程中其实还有诸多限制,如果使用不当,很可能出现死循环,抛异常等问题,例如:如果在用ListIterator逆序遍历的时候新增,会造成死循环。

  因此在学习迭代器的时候,不仅要掌握其使用方法,还要明白其实现原理。看源码是不错的学习办法,有兴趣的可以看看Collection的众多实现类对迭代的实现。

Java集合迭代器 Iterator分析的更多相关文章

  1. java集合---迭代器iterator

    一:ArraryList  最终继承超级接口Collection,Colection接口继承Iterator接口. public interface Collection<E> exten ...

  2. java集合源码分析(三):ArrayList

    概述 在前文:java集合源码分析(二):List与AbstractList 和 java集合源码分析(一):Collection 与 AbstractCollection 中,我们大致了解了从 Co ...

  3. java集合源码分析(六):HashMap

    概述 HashMap 是 Map 接口下一个线程不安全的,基于哈希表的实现类.由于他解决哈希冲突的方式是分离链表法,也就是拉链法,因此他的数据结构是数组+链表,在 JDK8 以后,当哈希冲突严重时,H ...

  4. Java 集合源码分析(一)HashMap

    目录 Java 集合源码分析(一)HashMap 1. 概要 2. JDK 7 的 HashMap 3. JDK 1.8 的 HashMap 4. Hashtable 5. JDK 1.7 的 Con ...

  5. Java集合源码分析(三)LinkedList

    LinkedList简介 LinkedList是基于双向循环链表(从源码中可以很容易看出)实现的,除了可以当做链表来操作外,它还可以当做栈.队列和双端队列来使用. LinkedList同样是非线程安全 ...

  6. 【转载】Java集合容器全面分析

    转自:http://blog.csdn.net/garfielder007/article/details/52143803 简介: 集合类Collection不是Java的核心类,是Java的扩展类 ...

  7. Java集合源码分析(一)

    Java集合工具包位于Java.util包下,包含了很多常用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致可以分为如下五个部分:List列表.Set集合.Map映射.迭 ...

  8. Java集合源码分析

    Java集合工具包位于Java.util包下,包含了很多常用的数据结构,如数组.链表.栈.队列.集合.哈希表等.学习Java集合框架下大致可以分为如下五个部分:List列表.Set集合.Map映射.迭 ...

  9. Java 实现迭代器(Iterator)模式

    类图 /** * 自己定义集合接口, 相似java.util.Collection * 用于数据存储 * @author stone * */ public interface ICollection ...

随机推荐

  1. mysql导入数据库出现:Incorrect string value: '\xE7\x82\xB9\xE9\x92\x9F' for column 'chinese' at row 1

    mysql导入数据库出现:Incorrect string value: '\xE7\x82\xB9\xE9\x92\x9F' for column 'chinese' at row 1 使用 sho ...

  2. For循环中不可以嵌套RDD操作

    今天犯了一个致命理解错误,Spark中的RDD Map操作只是一个计算式的传递,并不是Action,也就是在for循环中不会产生真正的计算. 因此,如果for循环中出现了RDD的Map类似操作,都会引 ...

  3. 【bzoj2086】Blocks

    在洛谷上点了个Splay的tag想玩玩,结果看到这题…… #include<bits/stdc++.h> #define N 1000005 using namespace std; ty ...

  4. centos7.3安装caffe出现错误:/bin/ld: cannot find -lcblas /bin/ld: cannot find -latlas

    安装caffe时需要依赖库atlas,可使用yum -y install atlas-devel  安装,但是安装之后还是有可能出现错误: /bin/ld: cannot find -lcblas / ...

  5. 正则表达式、re、常用模块

    阅读目录 正则表达式 字符 量词 . ^ $ * + ? { } 字符集[][^] 分组 ()与 或 |[^] 转义符 \ 贪婪匹配 re 总结 正则 re 常用模块 namedtuple deque ...

  6. mysql数据库设计之三范式

    第一范式: 第二范式:   正解: 第三范式: 示例: 正解: BC范式: 示例: 正解:

  7. Laravel5.5新特性

    1.新的报错页面 报错更加美观,并标记显示出错误的代码 2.包的自动配置 在conposer.json文件中加入包中的配置,下载后就会自动配置到app.php 文件中,使用更方便 在之前的 Larav ...

  8. 使用 gulp 压缩图片

    请务必理解如下章节后阅读此章节: 安装 Node 和 gulp 使用 gulp 压缩 JS 压缩 图片文件可降低文件大小,提高图片加载速度. 找到规律转换为 gulp 代码 规律 找到 images/ ...

  9. Visual Studio Code更改语言

    参数地址:Visual Studio Code 设置Display Language介绍 在Visual Studio Code中使用快捷键Ctrl + Shift + P可以打开命令行 在local ...

  10. CF981C Useful Decomposition【树/思维】

    [链接]:CF [题意]:给定一棵树,要求拆成若干条简单路径,并且这些路径都经过一个公共节点.给出任意一个解决方案,如不存在输出No. [分析]: 因为是一棵树, 所以如果要求任意两条路线至少有一个公 ...