迭代器概述
迭代器原理
- 迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么就需要在每一个类中定义hasNext()和next()方法,这样做是可以的,但是会让整个集合体系过于臃肿,迭代器是将这样的方法向上抽取出接口(规则),然后在每个类的内部,定义自己迭代方式,这样做的好处有二,第一规定了整个集合体系的遍历方式都是hasNext()和next()方法,第二,代码由底层内部实现,使用者不用管怎么实现的,会用即可
迭代器用法
- Java中的Iterator功能比较简单,并且只能单向移动:
1. 使用方法iterator()要求容器返回一个Iterator。第一次调用Iterator的next()方法时,它返回序列的第一个元素。注意:iterator()方法是java.lang.Iterable接口,被Collection继承。
2. 使用next()获得序列中的下一个元素。
3. 使用hasNext()检查序列中是否还有元素。
4. 使用remove()将迭代器新返回的元素删除
-
代码实例
import java.util.ArrayList; import java.util.Iterator; //ArrayList存储字符串并遍历 public class Demo2_generic { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); Iterator<String> it = list.iterator(); while(it.hasNext()) { System.out.println(it.next()); } } }
运行结果:
a b c d
可以看到,Iterator可以不用管底层数据具体是怎样存储的,都能够通过next()遍历整个List。
但是,具体是怎么实现的呢?背后机制究竟如何呢?
迭代器机制
这里我们来看看Java里AbstractList实现Iterator的源代码:
public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> { protected AbstractList() {} ...... public Iterator<E> iterator() { return new Itr(); //返回一个迭代器 } private class Itr implements Iterator<E> { int cursor = 0; int lastRet = -1; int expectedModCount = modCount; public boolean hasNext() { //实现hasNext方法 return cursor != size(); } public E next() { //实现next方法 checkForComodification(); try { int i = cursor; E next = get(i); lastRet = i; cursor = i + 1; return next; } catch (IndexOutOfBoundsException e) { checkForComodification(); throw new NoSuchElementException(); } } public void remove() { //实现remove方法 if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { AbstractList.this.remove(lastRet); if (lastRet < cursor) cursor--; lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException e) { throw new ConcurrentModificationException(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
可以看到,实现next()是通过get(cursor),然后cursor++,通过这样实现遍历。
这部分代码不难看懂,唯一难懂的是remove操作里涉及到的expectedModCount = modCount;
在网上查到说这是集合迭代中的一种“快速失败”机制,这种机制提供迭代过程中集合的安全性。
从源代码里可以看到增删操作都会使modCount++,通过和expectedModCount的对比,迭代器可以快速的知道迭代过程中是否存在list.add()类似的操作,存在的话快速失败!
在上面的例子中加入一条语句:
package com.zuikc.generic; import java.util.ArrayList; import java.util.Iterator; //ArrayList存储字符串并遍历 public class Demo2_generic { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); Iterator<String> it = list.iterator(); while(it.hasNext()) { System.out.println(it.next()); list.add("s"); } } }
会抛出如下异常
a
Exception in thread "main"
java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:901)
at java.util.ArrayList$Itr.next(ArrayList.java:851)
at com.zuikc.generic.Demo2_generic.main(Demo2_generic.java:17)
ConcurrentModificationException翻译过来是并发修改异常.也就是在迭代器遍历期间不允许其他方法修改元素.
ConcurrentModificationException解决方法
这时我们就需要找到一个迭代器自身的方法,在list接口下我们可以查到一个ListIterator方法.如果想在遍历的过程中修改元素,就可以使用此方法.
ListIterator:系列表迭代器,允许程序员按任一方向遍历列表、迭代期间修改列表,并获得迭代器在列表中的当前位置.
代码如下:
package com.zuikc.generic; import java.util.ArrayList; import java.util.Iterator; import java.util.ListIterator; //ArrayList存储字符串并遍历 public class Demo2_generic { public static void main(String[] args) { ArrayList<String> list = new ArrayList<>(); list.add("a"); list.add("b"); list.add("c"); list.add("d"); ListIterator<String> it = list.listIterator(); while(it.hasNext()) { System.out.println(it.next()); it.add("s"); } System.out.println(list); } }
结果如下:
a
b
c
d
[a, s, b, s, c, s, d, s]