您的位置:首页 > 其它

for循环遍历删除数据的异常及modCount的作用

2017-08-24 11:21 417 查看
[b]这篇博文主要记录下面几个问题:[/b]
[b]1、for循环遍历能不能删除数据不报错。普通for循环(for(int i=0;i<list.size;i++)) 可以;加强型不可以(for(String str:list))原因下面会解释[/b]
[b]2、迭代器能不能删除数据。可以(如果你没有使用错误的话)错误的情况下面也会解释[/b]
    首先看for循环删除集合元素的情况:

1、普通for循环删除元素:

List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
for(int i=0;i<list.size();i++){
String temp = list.get(i);
if(temp.equals("one")){
list.remove(i);
}
}
System.out.println(list);

这段代码运行是不会报异常的,但是打印的内容有点问题,只删除了一个“one”还有一个“one”,导致的原因就是i++; 因为删除第一个元素“one”的时候,第二个元素“one”变成了第一个元素,也就是整体向前移动一位,删除源码:
public E remove(int index) {
rangeCheck(index);

modCount++;
E oldValue = elementData(index);

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

return oldValue;
}

也就是index=0了,而此时for循环里i++,i=1了,这是就遍历不到index=0的这个位置了,所以删除不了,但是稍微修改下还是可以删除的,代码做如下调整:

List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");
for(int i=0;i<list.size();i++){
String temp = list.get(i);
if(temp.equals("one")){
list.remove(i);
i--;
}
}
System.out.println(list);满足条件时添加i--;将下标回退下,就可以遍历到每个位置了;
2、加强型for循环删除元素:

List<String> list = new ArrayList<String>();
list.add("one");
list.add("one");
list.add("two");
list.add("three");
list.add("four");
list.add("five");
list.add("six");

for(String ss:list){
if(ss.equals("one")){
list.remove(ss);
}
}
System.out.println(list);这个代码是会报异常的:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.auto.zjl.thread.Test.main(Test.java:36)原因下面再说,接着看用迭代器的操作:
List<String> list = new ArrayList<String>();
        list.add("one");
        list.add("one");
        list.add("two");
        list.add("three");
        list.add("four");
        list.add("five");
        list.add("six");
        Iterator<String> it = list.iterator();
        while(it.hasNext()){
            String str = it.next();
            if(str.equals("one")){
                //1:迭代器删除 it.remove();
                //2:列表删除 list.remove(str);
            }
        }
        System.out.println(list);上面的两种删除方式 1:迭代器删除是可以成功的;2:列表删除是失败的,会报上面的那个异常
下面说说原因:

1、加强型for循环用的也是迭代器

2、迭代器的删除方法是有更新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();
}
}

可以看到这条语句expectedModCount = modCount;而上面那个异常则是 对这两个值的比较,不同的时候报的异常:
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}这里的迭代器用的也是list的remove:语句:ArrayList.this.remove(lastRet);源码:
public E remove(int index) {
rangeCheck(index);

modCount++;
E oldValue = elementData(index);

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

return oldValue;
}


可以看到  modCount++; 即每次都会修改这个值;现在回过头来看下:迭代器删除的那两种方式
//1:迭代器删除 it.remove();
//2:列表删除 list.remove(str);观察下上面的两个方法的源码就会发发现,迭代器的删除方法是有更新要比较的值的:expectedModCount = modCount;
而集合的删除是没有同步内部迭代器的值的;所以在使用迭代器删除的时候,误用了集合的删除方法是会报异常的,而加强型for循环用的就是迭代器,但是删除又只能用list.remove();所以是不行的;

但为什么每次修改都有记录modCount呢:ArrayList是非线程安全的,在操作过程中如果有别的线程修改集合内容,那集合数据可能就不准确了,这时候抛出异常避免继续执行可以看做是个快速失败的方法(个人理解)
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息