概述
目录
分析
首先
然后
最后
解决方案
扩充
首先我们来看错误案例
@Test
public void doSomething(){
ArrayList<String> list = new ArrayList<String>();
list.add("昨天");
list.add("今天");
list.add("明天");
for (String s : list){
if (s.equals("昨天")){
list.remove(s);
}
}
}
这个将会报错
java.util.ConcurrentModificationException
分析
首先
调用了三次add,我们来看list中add的源码(不会的可以看我关于源码博文的list讲解),最终调用了modCount++。这个modCount变量就是为了记录操作list的次数,详细可以看源码。
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
然后
我们都知道增强for循环,相当于是迭代器的语法糖。ArrayList中迭代器的方法源码
public Iterator<E> iterator() {
return new Itr();
}
//Itr为内部类
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; //赋值给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;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = ArrayList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[i++]);
}
// update once at end of iteration to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
增强for循环相当于在while里调用了hasnext方法以及调用了next的方法,最后调用了外部的remove方法(注意,不是内部类itr的方法,此时expectedModCount为3。
调用remove方法时的源码,实质调用了fastRemove,此刻modCount又加1变为了4
private void fastRemove(int index) {
modCount++;
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
}
最后
还记得上边循环调用了next方法吗,我们来看一下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];
}
里边有个checkForComodification()
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
而此刻modCount为4,expectedModCount为3,不相等,所以抛出异常。
解决方案
我们注意到,在Itr内部类中有个remove方法,其中有个代码让expectedModCount=modCount(代码注释标出)
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。标准代码如下:
@Test
public void doSomething(){
ArrayList<String> list = new ArrayList<String>();
list.add("昨天");
list.add("今天");
list.add("明天");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()){
if (iterator.next().equals("昨天")){
iterator.remove();
}
}
System.out.println(list);
}
扩充
这种机制成为“fail-fast”机制,也就是只要出现一个异常,程序及时止损不再往下运行。这种机制一般仅用于检测bug。
OK,今天也是一知半解的一天,如果对你有帮助,赶紧点赞收藏关注。
最后
以上就是会撒娇衬衫为你收集整理的为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充的全部内容,希望文章能够帮你解决为什么不建议用增强for循环删除列表中元素(通俗易懂版)分析然后最后解决方案扩充所遇到的程序开发问题。
如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。
发表评论 取消回复