使用for循环对ArrayList在遍历的时候进行删除会有什么问题
i是累加的,但list的长度是变短的,会导致有些元素不会被遍历到,从而漏删部分元素,特别是连续相同元素
List<String> list = new ArrayList(Arrays.asList("a", "b", "b", "c", "d"));
//普通for循环remove调用的是fastRemove(e)
for (int i=0;i<list.size();i++) {
String str = list.get(i);
if ("b".equals(str)) {
list.remove(str);
}
}
System.out.printf(list.toString());
---------------------------
abbcd
abcd
使用Iterator对List集合进行删除操作时会报什么异常
会抛出异常ConcurrentModificationException
List<String> list = new ArrayList(Arrays.asList("a", "b", "b", "c", "d"));
//foreach实际使用的也是迭代器next()实现遍历
for (String str : list) {
if ("b".equals(str)) {
list.remove(str);//iterator()
}
}
//迭代器调用的是迭代器remove(),会对expectedModCount = modCount进行赋值
Iterator<String> itr = list.iterator();
while (itr.hasNext()) {
String str = itr.next();
if ("b".equals(str)) {
list.remove(str);//itr.remove();
}
}
foreach实际等价于下面的while+next循环,但是它却使用的是ArrayList.remove(e)这就意味着它的next()会出现问,在下面的源码部分会讲解。
ArrayList ,AbstractList和iterator源码解析
AbstractList{
protected transient int modCount = 0;//修改次数,替换,删除,排序都算修改
listIterator(final int index);
}
//ArrayList继承AbstractList
ArrayList extends AbstractList{
//list.remvove(e)实际调用方法
fastRemove(e){
modCount++;//不执行checkForComodification
}
private class Itr implements Iterator<E>{//迭代器
int expectedModCount = modCount;//初始值
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
public E next() {
checkForComodification();//foreach()
return (E) elementData[lastRet = i];
}
public void remove() {
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = modCount;//关键地方
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
};
}
-
modCount:父类AbstractList定义的属性:修改次数,替换,删除,排序都算修改,这里主要是指定对现有数组长度、位置变化的修改次数,但是新增,update不算修改次数
-
expectedModCount :迭代器中期望的修改次数,这个值只有实现的迭代器才有,迭代器的增删改截断方法在操作时都会expectedModCount = modCount
-
checkForComodification:迭代器中的方法,每次增删改截断next()都会先做一次检查modCount != expectedModCount就会抛出异常ConcurrentModificationException
-
fastRemove:ArrayList.remove(e)实际调用的删除元素方法,它本身只做删除元素后的累加操作modCount++;并不会操作expectedModCount,因为它压根就不属于ArrayList及其父类的属性
总结
常规for遍历使用list.remove(i)实际上是使用的是 ArrayList.fastRemove
- ArrayList.fastRemove(i),它只增加modCount++标记修改次数
- iterator.remove(),它在删改操作时都会expectedModCount = modCount进行赋值操作
- iterator.next()每次执行时都会checkForComodification()检查,如果modCount != expectedModCount就会抛出异常ConcurrentModificationException
因此在普通for循环中不会抛出异常,只是遍历会遗漏,从而导致部分元素漏删
iterator遍历时如果不使用iterator自有的remove()由于使用的ArrayList.remove(e)修改了modCount,而没有执行iterator中的expectedModCount = modCount,从而在next()时执行checkForComodification抛出ConcurrentModificationException
ArrayList 在remove时如何避免ConcurrentModificationException
-
ArrayList.remove(e)时不使用迭代器iterator和foreach遍历,只用for(int i=0;i<list.size();i++)常规遍历
-
使用迭代器iterator,包括foreach遍历时使用iterator.remove()