代码改变世界

迭代器机制及并发修改异常的解决

2018-08-05 20:46  好好学习哟  阅读(599)  评论(0编辑  收藏  举报

迭代器概述

迭代器原理

  • 迭代器是对集合进行遍历,而每一个集合内部的存储结构都是不同的,所以每一个集合存和取都是不一样,那么就需要在每一个类中定义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]