ArrayList的遍历方式
引子:
我们来看一个问题,在对ArrayList进行遍历时,凡是涉及到使用迭代器的,都不能使用ArrayList的remove、add方法,例如下边的while循环中使用了iterator来取值,使用了集合的remove方法,报错如下:
public class demo2 {
public static void main(String[] args) {
ArrayList<String> strings = new ArrayList<>();
strings.add("1");
strings.add("2");
strings.add("3");
Iterator<String> iterator = strings.iterator();
while (iterator.hasNext()){
String next = iterator.next();
strings.remove(next);
}
System.out.println(strings);
}
}
执行结果:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:911)
at java.util.ArrayList$Itr.next(ArrayList.java:861)
at demo2.main(demo2.java:17)
编译后的结果:
public class demo2 {
public demo2() {
}
public static void main(String[] args) {
ArrayList<String> strings = new ArrayList();
strings.add("1");
strings.add("2");
strings.add("3");
System.out.println(strings);
Iterator iterator = strings.iterator();
while(iterator.hasNext()) {
String next = (String)iterator.next();
strings.remove(next);
}
System.out.println(strings);
}
}
原因(集合的循环):
在ArrayList中有一个成员变量是modCount,该变量记录了Arraylist对象了操作次数,增删操作都会使modCount自增。在ArrayList
的内部,有一个迭代器的内部实现类,该类中有expectedModCount变量,其初始值为modCount在循环前的值。
1、迭代器中的next()方法
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];
}
next()方法中,有一个checkForComodification()方法,在该方法中会判断modCount 和 expectedModCount是否相等,如果不等,就抛出错误
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
所以如果在循环中使用remove\add方法,modCount就会自增,但是expectedModCount没变,就是抛出异常
2、那为什么可以使用迭代器中的remove方法?
ArrayList的迭代器内部类的remove方法:
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();
}
}
在该方法中,每次remove后,都会对expectedModCount重新赋值,所以不会出现expectedModCount != modCount的情况