在迭代循环遍历时删除某个数据,发现以下两种现象:使用集合本身的remove方法会报错,而使用iterator迭代器的remove方法不会出错;
使用集合本身的remove方法:
public class MyList {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
Iterator<String> iterator=list.iterator();
while(iterator.hasNext()){
String item=iterator.next();
if(item.equals("B")){
list.remove(item);
}
System.out.println(item);
}
}
}
使用list.remove(),会报一个java.util.ConcurrentModificationException的异常错误:
使用迭代器的remove方法:
public class MyList {
public static void main(String[] args) {
List<String> list=new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
list.add("D");
list.add("E");
Iterator<String> iterator=list.iterator();
while(iterator.hasNext()){
String item=iterator.next();
if(item.equals("B")){
//list.remove(item);
iterator.remove();
}
System.out.println(item);
}
System.out.println(list);
}
}
使用iterator.remove()不会出错;
通过观察源码可知:
在AbstractList的源码中:每一次对集合进行add、set、get、remove操作都会进行一次checkForComodification();校验操作:
public E set(int index, E element) {
rangeCheck(index);
checkForComodification();
return l.set(index+offset, element);
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return l.get(index+offset);
}
public int size() {
checkForComodification();
return size;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
checkForComodification();
l.add(index+offset, element);
this.modCount = l.modCount;
size++;
}
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = l.remove(index+offset);
this.modCount = l.modCount;
size--;
return result;
}
而checkForComodification()方法中的操作是:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
那modCount和expectedModCount是什么呢?
在ArrayList的源码中可以发现:
在ArrayList中每进行一次remove、add操作,modCount都会加1:
由此可以猜测modCount是保存对集合进行的操作次数的变量;
在迭代器Iterator中,
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;//先将modCount赋值给expectedModCount,保存对集合的操作次数
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;//在此处更新了expectedModCount,所以不会出错
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
可以发现,modCount存在于AbstractList中,记录集合被修改(add、remove)的次数;在迭代器中,进行list.iterator()时,首先会将修改次数modCount赋值给迭代器中的expectedModCount,用expectedModCount来存储当前集合修改次数;
而如果在迭代循环的过程中:
如果使用了list.remove方法,就会将modCount+1,而后再进行checkForComodification()方法时,就会发现modCount与expectedModCount不相等,就会抛出异常 ConcurrentModificationException;
如果使用了Iterator的remove方法,在Iterator的remove方法中可以发现,它将expectedModCount进行了更新: expectedModCount = modCount;expectedModCount 与modCount相等,就不会报错了;
可以说,迭代器就是当前集合的一个副本。