`
编程足球
  • 浏览: 251126 次
  • 性别: Icon_minigender_1
  • 来自: 福州
社区版块
存档分类
最新评论

List 遍历 ConcurrentModificationException

    博客分类:
  • java
 
阅读更多
有如下代码片段:
public static void main(String[] args) {
		List<Integer> list = new ArrayList<Integer>();
		list.add(1);
		list.add(2);
		list.add(3);
		list.add(4);
		list.add(5);
		
        // 依次输出结果
		for (Integer integer : list) {
			System.out.println(integer);
		}
	}


但是如果在遍历的过程中有remove()方法,则会报错
for (Integer integer : list) {
	System.out.println(integer);
	if(integer.intValue() == 2){
	     list.remove(integer);
	}
}


Exception in thread "main" java.util.ConcurrentModificationException
	at java.util.AbstractList$Itr.checkForComodification(Unknown Source)
	at java.util.AbstractList$Itr.next(Unknown Source)
	at study.lgf.study.Foreach.main(Foreach.java:23)


其实在使用foreach的时候系统采用如下方式解析:
for (Iterator<Integer> iter = list.iterator(); iter.hasNext();) {
	Integer integer = iter.next();
	System.out.println(integer);
	if(integer.intValue() == 2){
		list.remove(integer);
	}
}




关于Iterator的源码
    private class Itr implements Iterator<E> {
    	
    	// 当前指针位子
    	int cursor = 0;

    	// iter.next(); 对应的值的位置
    	int lastRet = -1;

    	// list.iterator() 时候List修改的次数
    	int expectedModCount = modCount;

    	// 通过判断size和cursor来判断是否还有数据
    	public boolean hasNext() {
                return cursor != size();
    	}

    	/**
    	 * 1. 判断是否被修改过,如果被修改过则直接报错
    	 * 2. 获得当前下标对应的值
    	 * 3. lastRet = cursor++; 保存当前位置,并且指向下一个
    	 * 4. 返回获得的值
    	 */
    	public E next() {
                checkForComodification();
    	    try {
    		E next = get(cursor);
    		lastRet = cursor++;
    		return next;
    	    } catch(IndexOutOfBoundsException e) {
    		checkForComodification();
    		throw new NoSuchElementException();
    	    }
    	}

    	/**
    	 * 1. 直接移除当前位置的对象
    	 * 2. 指针往后倒一位lastRet < cursor
    	 * 3. lastRet = -1; 当前位置已删除,如果再次移除则直接报错
    	 * 4. expectedModCount = modCount;  同步修改次数
    	 */
    	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();
    	}
    }



由此可以知道
java.util.ConcurrentModificationException
的报错原因是应为List在编译过程中修改了,不管是remove,add还是其他什么操作


正确的解决方案就是通过内置的删除方法
/**
 * 通过iterator来遍历,可以正常删除
 * 但是在遍历过程中不能对list进行编辑操作,否则会报错
 */
for (Iterator<Integer> iter = list.iterator(); iter.hasNext();) {
	Integer integer = iter.next();
	System.out.println(integer);
	if(integer.intValue() == 2){
		iter.remove();
	}
}


/**
 * 通过普通的for循环,在移除时候i--(remove时候所有数据向前移动一位)
 * 而且如果在遍历过程中使用add(i),也可以正常遍历到.
 * 但是如果list.add(index, element)则很容易出错 看index和i的关系
 * 如果i>index则可能导致重复遍历一次,i<index....
 */
for (int i = 0; i < list.size(); i++) {
	Integer integer = list.get(i);
	System.out.println(integer);
	if(integer.intValue() == 3){
		list.remove(integer);
		i--;
	}
}

// 建议使用第一种,编译时候锁定list,不在对其进行编辑操作
分享到:
评论

相关推荐

Global site tag (gtag.js) - Google Analytics