Java源码研究001:关于ArraytList/ List的并发修改异常
这个就是实现一个简单的 ArrayList 的遍历,如果存在一个为“aaa”的值,就添加一个“ccc”
package Array; import java.util.ArrayList; import java.util.Iterator;
public class demo2 { public static void main(String[] args) { ArrayList<String> list = new ArrayList<String>(); list.add("aaa"); list.add("bbb"); demo2.bianli(list); } public static void bianli(ArrayList<String> list) { Iterator<String> it = list.iterator(); // 1.迭代器:java.util.ConcurrentModificationException // while(it.hasNext()) { // String s = it.next(); // if(s.equals("aaa")) { // list.add("ccc"); // } // } // 2.加强for循环:java.util.ConcurrentModificationException // for(String s : list) { // if(s.equals("aaa")) { // list.add("ccc"); // } // } // 3.普通for循环可以,因为用的是:list.get(i),用 it.next()就出错 // 因为iterator源码里要比对一个东西 for (int i = 0; i < list.size(); i++) { String s = list.get(i); if (s.equals("aaa")) { list.add("ccc"); } } System.out.println(list); } }
从上面的代码中可以发现,使用list.get(i)就可以完成需求,而使用it.next();就不行
我们看一下it.next()出了甚么问题
首先根据
Iterator<String> it = list.iterator();
我们追踪到list中的iterator的方法
package java.util; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { 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; // prevent creating a synthetic constructor Itr() {} public boolean hasNext() { return cursor != size; } @SuppressWarnings("unchecked") 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(); } } @Override public void forEachRemaining(Consumer<? super E> action) { Objects.requireNonNull(action); final int size = ArrayList.this.size; int i = cursor; if (i < size) { final Object[] es = elementData; if (i >= es.length) throw new ConcurrentModificationException(); for (; i < size && modCount == expectedModCount; i++) action.accept(elementAt(es, i)); // update once at end to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } } final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
此时发现了一个CheckForComodification()函数,单独拿出来看一下
CheckForComodification()中我们看到抛出了ConcurrentModificationException的错误的可能性是 条件 modCount != expectedModCount 为真
package java.util; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { final void checkForComodification() { if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
好的我们继续debug,发现都是正常的,但是当if判断中条件为真,要执行
list.add("ccc");
的时候,我们追踪到这个list中的add方法
package java.util; public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { public boolean add(E e) { modCount++; add(e, elementData, size); return true; } }
我们可以看到modCount++,modCount的值被改变了,这就是modCount != expectedModCount 为真的情况
expected的意思是期望,也就是说期望值和实际值不同导致错误。
那么怎么解决,最简单就是不使用it.next()
而使用list.get()!