java.util.ConcurrentModificationException

来源:互联网 发布:做账软件免费 编辑:程序博客网 时间:2024/06/05 21:16

1 what

使用的数据集合:

List myList = new ArrayList();
myList.add( “1”);
myList.add( “2”);
myList.add( “3”);
myList.add( “4”);
myList.add( “5”);

以下三种情况都会出现异常:

Iterator it = myList.iterator();
while (it.hasNext()) {
String value = it.next();
if (value.equals( “3”)) {
myList.remove(value); // error
}
}

for (Iterator it = myList.iterator(); it.hasNext();) {
String value = it.next();
if (value.equals( “3”)) {
myList.remove(value); // error
}
}

for (String value : myList) {
System. out.println( “List Value:” + value);
if (value.equals( “3”)) {
myList.remove(value); // error
}
}

异常信息如下:

Exception in thread “main” java.util.ConcurrentModificationException
at java.util.AbstractListItr.checkForComodification(UnknownSource)atjava.util.AbstractListItr.next(Unknown Source)

2 why 为什么回出现这个问题

以上都有3种出现异常的情况有一个共同的特点,都是使用Iterator进行遍历,且都是通过ArrayList.remove(Object) 进行删除操作。

想要找出根本原因,直接查看ArrayList源码看为什么出现异常:

(以ArrayList来讲解)在ArrayList中,它的修改操作(add/remove)都会对modCount这个字段+1,modCount可以看作一个版本号,每次集合中的元素被修改后,都会+1(即使溢出)。接下来再看看

AbsrtactList中iteraor方法
public Iterator iterator() {
return new Itr();
}
它返回一个内部类,这个类实现了iterator接口,代码如下:

[java] view plain copy
private class Itr implements Iterator {
int cursor = 0;

int lastRet = -1;  int expectedModCount = 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 的值          expectedModCount = modCount;          } catch (IndexOutOfBoundsException e) {          throw new ConcurrentModificationException();      }  }  final void checkForComodification() {      if (modCount != expectedModCount)          throw new ConcurrentModificationException();  }  }  

在内部类Itr中,有一个字段expectedModCount ,初始化时等于modCount,即当我们调用list.iterator()返回迭代器时,该字段被初始化为等于modCount。在类Itr中next/remove方法都有调用checkForComodification()方法,在该方法中检测modCount == expectedModCount,如果不相当则抛出异常ConcurrentModificationException。

前面说过,在集合的修改操作(add/remove)中,都对modCount进行了+1。
在看看刚开始提出的那段代码,在迭代过程中,执行list.remove(val),使得modCount+1,当下一次循环时,执行 it.next(),checkForComodification方法发现modCount != expectedModCount,则抛出异常。

3 how 怎么解决

这要分两种情况,单线程和多线程删除情况。

单线程情况:

1 使用Iterator提供的remove方法,用于删除当前元素
2 建一个集合,记录需要删除的元素,之后统一删除
3 使用线程安全CopyOnWriteArrayList进行删除操作
4 不使用Iterator进行遍历,需要注意的是自己保证索引正常

// 1 使用Iterator提供的remove方法,用于删除当前元素 for (Iterator<string> it = myList.iterator(); it.hasNext();) {     String value = it.next();      if (value.equals( "3")) {          it.remove();  // ok     }}System. out.println( "List Value:" + myList.toString()); // 2 建一个集合,记录需要删除的元素,之后统一删除             List<string> templist = new ArrayList<string>(); for (String value : myList) {      if (value.equals( "3")) {          templist.add(value);     }} // 可以查看removeAll源码,其中使用Iterator进行遍历myList.removeAll(templist);System. out.println( "List Value:" + myList.toString());          // 3. 使用线程安全CopyOnWriteArrayList进行删除操作List<string> myList = new CopyOnWriteArrayList<string>();myList.add( "1");myList.add( "2");myList.add( "3");myList.add( "4");myList.add( "5");Iterator<string> it = myList.iterator(); while (it.hasNext()) {     String value = it.next();      if (value.equals( "3")) {          myList.remove( "4");          myList.add( "6");          myList.add( "7");     }}System. out.println( "List Value:" + myList.toString()); // 4. 不使用Iterator进行遍历,需要注意的是自己保证索引正常 for ( int i = 0; i < myList.size(); i++) {     String value = myList.get(i);     System. out.println( "List Value:" + value);      if (value.equals( "3")) {          myList.remove(value);  // ok          i--; // 因为位置发生改变,所以必须修改i的位置     }}System. out.println( "List Value:" + myList.toString());

多线程异常情况

上述解决方案,在多线程的情况下,仍然会出现问题。

推荐使用ConcurrentHashMap或者CopyOnWriteArrayList。

4 参考链接

http://blog.csdn.net/smcwwh/article/details/7036663
http://www.2cto.com/kf/201403/286536.html

0 0
原创粉丝点击