首先大家先看一段代码:

public static void main(String[] args) {

List<String> listStr = new ArrayList<String>();
      listStr.add("1");
      listStr.add("2");
      listStr.add("3");
      listStr.add("4");
      listStr.add("5");

System.out.println("listStr.size:::"+listStr.size());

for(String str:listStr){
         if("3".equals(str)){
             listStr.remove(str);

}
      }

System.out.println("listStr.size:::"+listStr.size());

现象:

该程序会抛出一个  java.util.ConcurrentModificationException异常。

分析:

对 Collection 或 Map 进行迭代操作过程中尝试直接修改 Collection / Map 的内容时,会抛出java.util.ConcurrentModificationException 异常。 因为在修改数据时不能同步原有list中数据,当下一次循环时找不到数据。

解决办法:

Iterator 在工作的时候是不允许被迭代的对象被改变的,使用 Iterator 本身的方法 remove() 来删除对 
象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

改后代码:

public static void main(String[] args) {

List<String> listStr = new ArrayList<String>();
      listStr.add("1");
      listStr.add("2");
      listStr.add("3");
      listStr.add("4");
      listStr.add("5");

System.out.println("listStr.size:::"+listStr.size());

Iterator<String> ite = listStr.iterator();
      while(ite.hasNext()){
          String str = (String)ite.next();
          if("3".equals(str)){
             ite.remove();
          }
      }

System.out.println("listStr.size:::"+listStr.size());

}

补充:

或许有人可能会问如果我不用增强for循环,直接用.get(index)方法会不会报错?

代码如下:

for(int i = 0 ; i < listStr.size(); i++){
           if("3".equals(listStr.get(i))){
               listStr.remove(i);
           }
      }

现象:

listStr.size:::5
        listStr.size:::4

大家可以看到通过list的下表索引来修改list数据是不会出错的。

分析:

大家可以这样认为:通过所引来循环时,jvm记录的是所引值,当移除当前对象后,其他元素的索引号不会同步改变。下次循环仍可以找到对应数据。而增强for循环,记录的是当前对象,当下次循环时,会先找到该对象,然后游标向下移,这时候找不到对象所以结果会混乱。

java.util.ConcurrentModificationException

【引言】

经常在迭代集合元素时,会想对集合做修改(add/remove)操作,类似下面这段代码:

  1. for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {
  2. Integer val = it.next();
  3. if (val == 5) {
  4. list.remove(val);
  5. }
  6. }

运行这段代码,会抛出异常java.util.ConcurrentModificationException。

【解惑】

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

  1. public Iterator<E> iterator() {
  2. return new Itr();
  3. }

它返回一个内部类,这个类实现了iterator接口,代码如下:

  1. private class Itr implements Iterator<E> {
  2. int cursor = 0;
  3. int lastRet = -1;
  4. int expectedModCount = modCount;
  5. public boolean hasNext() {
  6. return cursor != size();
  7. }
  8. public E next() {
  9. checkForComodification();
  10. try {
  11. E next = get(cursor);
  12. lastRet = cursor++;
  13. return next;
  14. } catch (IndexOutOfBoundsException e) {
  15. checkForComodification();
  16. throw new NoSuchElementException();
  17. }
  18. }
  19. public void remove() {
  20. if (lastRet == -1)
  21. throw new IllegalStateException();
  22. checkForComodification();
  23. try {
  24. AbstractList.this.remove(lastRet);
  25. if (lastRet < cursor)
  26. cursor--;
  27. lastRet = -1;
  28. // 修改expectedModCount 的值
  29. expectedModCount = modCount;
  30. } catch (IndexOutOfBoundsException e) {
  31. throw new ConcurrentModificationException();
  32. }
  33. }
  34. final void checkForComodification() {
  35. if (modCount != expectedModCount)
  36. throw new ConcurrentModificationException();
  37. }
  38. }

在内部类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,则抛出异常。

【解决办法】
如果想要在迭代的过程中,执行删除元素操作怎么办?
再来看看内部类Itr的remove()方法,在删除元素后,有这么一句expectedModCount = modCount,同步修改expectedModCount 的值。所以,如果需要在使用迭代器迭代时,删除元素,可以使用迭代器提供的remove方法。对于add操作,则在整个迭代器迭代过程中是不允许的。 其他集合(Map/Set)使用迭代器迭代也是一样。

    //获奖显示
public String prizeList(){
voteList = userWorksService.getAllRank();
UserWorks userWorks = new UserWorks();
Iterator<UserWorks> list = voteList.iterator();
Integer rank ;
while(list.hasNext()){
userWorks = list.next();
if(!("9".equals(userWorks.getAwardsRank()))){
rank = Integer.parseInt(userWorks.getAwardsRank())+2;
userWorks.setAwardsRank(rank +"");
allList.add(userWorks);
}
}
voteList.removeAll(allList);
return "prizeList";
}

list删除操作 java.util.ConcurrentModificationException的更多相关文章

  1. 再次踩bug:遍历删除list(java.util.ConcurrentModificationException)

    再次踩bug:遍历删除list(java.util.ConcurrentModificationException) 使用 List<Long> list = new ArrayList& ...

  2. 为什么阿里巴巴禁止在 foreach 循环里进行元素的 remove/add 操作--java.util.ConcurrentModificationException

    摘要 foreach循环(Foreach loop)是计算机编程语言中的一种控制流程语句,通常用来循环遍历数组或集合中的元素. 在阿里巴巴Java开发手册中,有这样一条规定: 但是手册中并没有给出具体 ...

  3. 遍历List过程中删除操作报java.util.ConcurrentModificationException错误

    1:遍历List 同时 remove 元素,出现java.util.ConcurrentModificationException错误 @Test public void test3(){ List& ...

  4. 对ArrayList操作时报错java.util.ConcurrentModificationException null

    用iterator遍历集合时要注意的地方:不可以对iterator相关的地方做添加或删除操作.否则会报java.util.ConcurrentModificationException 例如如下代码: ...

  5. Hashtable 删除元素, 抛出异常 java.util.ConcurrentModificationException

    今天在对一个Hashtable对象进行 搜索 -> 删除 操作时遇到的一个问题,开始的使用我使用的是Hashtable的Iterator,然后直接执行: Hashtable.remove(key ...

  6. 集合循环删除问题-报错java.util.ConcurrentModificationException解析

    java.util.ConcurrentModificationException 异常问题详解 环境:JDK 1.8.0_111 在Java开发过程中,使用iterator遍历集合的同时对集合进行修 ...

  7. JAVA循环迭代中删除或添加集合数据报java.util.ConcurrentModificationException错误

    1.写出下面的输出结果 public class test{ public static void main(String [] args) List<String> list = new ...

  8. SpringMvc中Hashmap操作遇到 java.util.ConcurrentModificationException: null

    代码按照网上修改为类似,还不能解决问题 for (Iterator<String> it = target.keySet().iterator(); it.hasNext(); ) { i ...

  9. java.util.ConcurrentModificationException 解决办法(转载)

    今天在项目的中有一个需求,需要在一个Set类型的集合中删除满足条件的对象,这时想当然地想到直接调用Set的remove(Object o)方法将指定的对象删除即可,测试代码:   public cla ...

随机推荐

  1. LeetCode Best Time to Buy and Sell Stock 买卖股票的最佳时机 (DP)

    题意:给定一个序列,第i个元素代表第i天这支股票的价格,问在最佳时机买入和卖出能赚多少钱?只买一次,且仅1股,假设本钱无限. 思路:要找一个最低价的时候买入,在最高价的时候卖出利润会最大.但是时间是不 ...

  2. Oracle IO优化心得

    很多的时侯,做Oracle DBA的我们,当应用管理员向我们通告现在应用很慢.数据库很慢的时侯,我们到数据库时做几个示例的Select也发现同样的问题时,有些时侯我们会无从下手,因为我们认为数据库的各 ...

  3. windows装liunx双系统

    http://jingyan.baidu.com/article/60ccbceb18624464cab197ea.html http://jingyan.baidu.com/article/76a7 ...

  4. Web开发人员必备工具-Emmet (Zen Coding)

    如果你从事前端开发或者web开发的话,一定听说过Zen coding - 一种快速编写HTML/CSS代码的方法.它使用仿CSS选择器的语法来快速开发HTML和CSS - 由Sergey Chikuy ...

  5. CF 577B Modulo Sum

    题意:给一个长度为n的正整数序列,问能不能找到一个不连续的子序列的和可以被m整除. 解法:抽屉原理+dp.首先当m<n时一定是有答案的,因为根据抽屉原理,当得到这个序列的n个前缀和%m时,一定会 ...

  6. HTTP协议中的长连接和短连接(keep-alive状态)

    什么是长连接 HTTP1.1规定了默认保持长连接(HTTP persistent connection ,也有翻译为持久连接),数据传输完成了保持TCP连接不断开(不发RST包.不四次握手),等待在同 ...

  7. 获取json中字段,判断是否有想要的key

    if(json.containsKey("key")){ String refundSid = json.getString("key"); } 如果也要判断v ...

  8. Ubuntu14.04下Python3.4启动IDLE

    1.在Ubuntu14.04 LTS版本中,已经自行安装了python,可以在Terminal(CTRL+ALT+T)中输入:ls /usr/bin | grep python 进行查看. 如果想运行 ...

  9. C#复习反射

    反射中常用方法: //获取对象类型 One one = new One(); Type t = one.GetType(); //动态加载 Assembly a = Assembly.LoadFile ...

  10. C++实现网格水印之调试笔记(六)——补充

    调用matlab生成的网格水印特征向量矩阵 从文件中读取的原始网格的特征向量矩阵 好吧,之前得出的结果不正确是因为代码写错了.因为实现论文中的提取方案时代码写错了,自己想了另外一个方法,结果方向两者在 ...