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

这篇博文主要记录下面几个问题:

1、for循环遍历能不能删除数据不报错。 

        普通for循环(for(int i=0;i<list.size;i++)) 可以;加强型不可以(for(String str:list))原因下面会解释

2、迭代器能不能删除数据。

        可以(如果你没有使用错误的话)错误的情况下面也会解释

首先看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是非线程安全的,在操作过程中如果有别的线程修改集合内容,那集合数据可能就不准确了,这时候抛出异常避免继续执行可以看做是个快速失败的方法(个人理解)

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值