在遍历中,如果出现了意料之外的修改,会抛出ConcurrentModificationException,这个机制主要目的是避免多个线程对同一个集合的内容进行操作而导致集合内数据的混乱。但是这个机制会给我们带来一些困扰
比如使用增强for循环遍历数组同时进行修改时
List<Integer> numList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); for (Integer i : numList) { if (i == 3) { 下面两个语句都会报错 numList.add(3); numList.remove(3); } }
它的机制是这样的,集合中持有一个int类型的字段modCount,集合内的数据发生变动(增加,删除,排序等等)时,modCount 会改变
public E remove(int index) {
rangeCheck(index);
modCount++; 看到modCount变化了吧
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
集合的某些方法里(遍历,排序等等)会检查expectedModCount和modCount是否相等,如果不相等,说明在方法在运行的过程中数据发生了变动,就会抛出ConcurrentModificationException。
public void sort(Comparator<? super E> c) {
final int expectedModCount = modCount; 先让它们俩相等
Arrays.sort((E[]) elementData, 0, size, c);
if (modCount != expectedModCount) { 如果modCount发生了变动,抛异常
throw new ConcurrentModificationException();
}
modCount++; 数据变动后,修改modCount
}
如果我们想在遍历集合的同时对集合进行修改,我们就得使用不会检查modCount的遍历方式。
下面的方法可以满足这样的需求
List<Integer> numList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)); 方式一 Iterator<Integer> it = numList.iterator(); while(it.hasNext()) { if (it.next() == 3) { it.remove(); } } 方式二 for (int i = 0; i < numList.size(); i++) { if (numList.get(i) == 3) { numList.remove(i); } }
网友评论