集合:Iterator

why ? when ? how ? what ?

为什么需要集合呢?

在数据结构中链表、树、堆等一些操作都是由我们自己写的,这些操作是不是可以提取出来,以后要用就直接拿来用就好,这样非常方便。

Java 集合框架图

由上图我们可以看到,Java 集合主要分为两类:Collection 和 Map。

Collection 接口继承了 Iterable 接口。而 Iterable 接口中主要方法是 iterator();

public interface Iterable<T> {
/**
 * Returns an iterator over elements of type {@code T}.
 *
 * @return an Iterator.
 */
Iterator<T> iterator();
} 

废弃的接口:Enumeration
Enumeration 接口是JDK1.0时推出的,是最好的迭代输出接口,最早使用Vector(现在推荐使用ArrayList)时就是使用Enumeration接口进行输出。

Enumeration接口常用的方法有hasMoreElements()(判断是否有下一个值)和 nextElement()(取出当前元素),这些方法的功能跟Iterator类似,只是Iterator中存在删除数据的方法,而此接口不存在删除操作。

Iterator

Iterator 接口含有如下 4 个方法

看到 default 关键字,网上看了下是 jdk 1.8 出来的,就是接口中可以包含方法体。

fail-fast 机制是java集合中的一种错误机制。当多个线程对同一个集合的内容进行操作时,就可能会产生fail-fast事件。例如:当某一个线程A通过iterator去遍历某集合的过程中,若该集合的内容被其他线程所改变了;那么线程A访问集合时,就会抛出
ConcurrentModificationException异常,产生fail-fast事件。

for-each 的循环内部也是使用了 Iterator 来遍历Collection,它也调用了 Iterator.next(),所以在修改元素时会检查(元素的)变化并抛出 ConcurrentModificationException。

Iterator<Integer> iterator=list.iterator();
    while (iterator.hasNext()){
        list.remove(2);
        System.out.println(iterator.next());
    }

会抛出 currentModificationException 因为我们在使用 Iterator 对容器进行迭代的时候修改了容器。

如 ArrayList 中 内部类实现 Iterator

private class Itr implements Iterator<E> {
    int cursor;       // index of next element to return  表示下一个要访问的元素的索引
    int lastRet = -1; // index of last element returned; -1 if no such 表示上一个访问的元素的索引
    int expectedModCount = modCount;//这个就是为了验证现在遍历的容器是不是没被修改过
}

modCount 是指集合修改次数

modCount 在 add, clear, remove 时都会被修改。

final void checkForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

 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;
        return (E) elementData[lastRet = i];
    }

    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();
        }
    }

在调用迭代器的 next、remove方法都会检查 modCount、expectedModCount 这两个的值是否相等。

如何解决 currentModificationException?

单线程:使用 Iterator 中的 remove 方法

多线程:

由于使用iterator对容器进行访问不需要获取锁,在多线程中就会造成当一个线程删除了元素,由于 modCount 是 AbstarctList 的成员变量,因此可能会导致在其他线程中modCount 和 expectedModCount值 不等。

static ArrayList<Integer> list = new ArrayList<Integer>();
public static void main(String[] args)  {
    list.add(1);
    list.add(2);
    list.add(3);
    list.add(4);
    list.add(5);
    Thread thread1 = new Thread(){
        @Override
        public void run() {
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext()){
                Integer integer = iterator.next();
                System.out.println(integer);
                try {
                    Thread.sleep(100);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };
    };
    Thread thread2 = new Thread(){
        @Override
        public void run() {
            Iterator<Integer> iterator = list.iterator();
            while(iterator.hasNext()){
                Integer integer = iterator.next();
                if(integer==2)
                    iterator.remove();
            }
        };
    };
    thread1.start();
    thread2.start();
}

一般有2种解决办法:

  1)在使用iterator迭代的时候使用synchronized或者Lock进行同步;

  2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。

参考

https://blog.csdn.net/u011240877/article/details/52743564

https://www.cnblogs.com/andy-zhou/p/5339683.html

posted @ 2018-08-02 12:47  罗贱人  阅读(222)  评论(0编辑  收藏  举报