JAVA中循环遍历list有三种方式for循环、foreach循环、iterator遍历。
1、for循环遍历list
for(int i = 0; i < list.size(); i++){
if(list.get(i).equals("del")){
list.remove(i);
}
}
这种方式的问题在于,删除某个元素后,list的大小发生了变化,索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。
2、foreach循环
for(String x:list){
if (x.equals("del"))
list.remove(x);
}
}
这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。
3、iterator遍历
Iterator<String> it = list.iterator();
while(it.hasNext()){
String x = it.next();
if (x.equals("del")) {
it.remove();
}
}
}
这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。
总结:
(1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种
(2)循环删除list中多个元素的,应该使用迭代器iterator方式。
分析java.util.ConcurrentModificationException,环境:jdk1.8.0
AbstractList是ArrayList的父类,这两个类都有iterator()方法
AbstractList代码片段:
protected transient int modCount = 0;
表示对List的修改次数,每次调用add()方法或者remove()方法就会对modCount进行加1操作
If the value of this field changes unexpectedly, the iterator (or list
iterator) will throw a {@code ConcurrentModificationException} in
response to the {@code next}, {@code remove}, {@code previous},
{@code set} or {@code add} operations.
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor = 0;//表示下一个要访问的元素的索引
int lastRet = -1;//表示上一个访问的元素的索引
int expectedModCount = modCount;//表示对ArrayList修改次数的期望值,它的初始值为modCount。
public boolean hasNext() {
return cursor != size();
}
public E next() {
checkForComodification();
try {
E next = get(cursor);
lastRet = cursor++;
return next;
} catch (IndexOutOfBoundsException e) {
checkForComodification();
throw new NoSuchElementException();
}
}
public void remove() {
if (lastRet == -1)
throw new IllegalStateException();
checkForComodification();
try {
AbstractList.this.remove(lastRet);
if (lastRet < cursor)
cursor--;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException e) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
ArrayList的remove():
public boolean remove(Object o) {
if (o == null) {
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
if (o.equals(elementData[index])) {
fastRemove(index);
return true;
}
}
return false;
}
private void fastRemove(int index) {
modCount++;//对集合修改了一次
int numMoved = size - index - 1;//remove后的数量
if (numMoved > 0)
/**
*the source array.
*starting position in the source array.
*the destination array.
*starting position in the destination data.
*the number of array elements to be copied.
*/
System.arraycopy(elementData, index+1, elementData, index,numMoved);
elementData[--size] = null; // Let gc do its work
}
原因:调用list.remove()方法导致modCount和expectedModCount的值不一致。
使用for-each进行迭代也会出现这种问题。
iterator的remove方法调用的就是list.remove()方法,但是它多了一个操作:
expectedModCount = modCount;
多线程的情况下,使用iterator也会出问题。
建议:使用并发CopyOnWriteArrayList代替ArrayList和Vector。
网友评论