ArrayList之坑点

来源:互联网 发布:算法导论第四版 pdf 编辑:程序博客网 时间:2024/05/17 02:15

ConcurrentModificationException异常

详细原因参见 http://www.cnblogs.com/dolphin0520/p/3933551.html

缘起iterator

简单来说:在iterator中遍历以及删除时都会去执行如下检查

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

modCount 就是modify count 对队列修改的次数。

那么expectedModCount是什么时候赋予的呢?

 public Iterator<E> iterator() {        return new Itr();    }    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;

在Iterator对象初始化的时候就赋予了当前的modCount值,如果这之后在执行iterator.next() 的同时,还去调用list.add或remove操作,那么肯定会出现expectedModCount != modCount的情况,从而导致异常产生。

仅仅只是iterator?牵出foreach

被广泛使用的foreach,其实间接的就是使用iterator实现的。(在JDK中没法看到具体实现,反编译可以看到字节码窥探到) 参见 http://blog.csdn.net/a596620989/article/details/6930479

为什么作者要这么做呢?

在遍历的时候他不希望队列在别的线程里还进行队列的修改,也就是说在设计的时候没有考虑多线程同步问题,然后用这种条件来限制。

该如何解决

很弱的办法

在删除的时候使用iterator的remove ,因为他进行删除的时候,会同步一下modCount的值给expectedModCount。

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

这个只能说解决了一小部分问题。遇到多线程还是有问题。

最后有2种解决办法:

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

用这种同步锁的方式来阻止modCount不一致的情况发生。这个办法直接,但是需要自己手动添加锁。

 2)使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。见http://blog.csdn.net/xude1985/article/details/51418558

addAll

public boolean addAll(Collection<? extends E> c) {        Object[] a = c.toArray();        int numNew = a.length;        ensureCapacityInternal(size + numNew);  // Increments modCount        System.arraycopy(a, 0, elementData, size, numNew);        size += numNew;        return numNew != 0;    }

虽然我们看到有arraycopy,但是他复制的只是对象引用,而作为被指向的目标:对象数据则是没有任何改变。addAll只是浅拷贝而已,并没有进行深拷贝,所以你在改变对象内部数据的时候,仍旧是一变都变的

add

 public boolean add(E e) {        ensureCapacityInternal(size + 1);  // Increments modCount!!        elementData[size++] = e;        return true;    }

add时候并没有给你进行去重,所以你需要在调用add前自己去重

0 0