zoukankan      html  css  js  c++  java
  • list删除操作 java.util.ConcurrentModificationException

    首先大家先看一段代码:

    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)操作,类似下面这段代码:

    [java] view plaincopy
     
    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方法

    [java] view plaincopy
     
    1. public Iterator<E> iterator() {  
    2.     return new Itr();  
    3. }  

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

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


    在内部类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";
        }
  • 相关阅读:
    web总结
    工作总结
    python积累
    学习地图
    position:absolute绝对定位解读
    利用C++ RAII技术自动回收堆内存
    C++封装常用对象和对头文件探索
    String.split()方法你可能不知道的一面
    动态内存分配(new)和释放(delete)
    C#实现的异步Socket服务器
  • 原文地址:https://www.cnblogs.com/a757956132/p/4146234.html
Copyright © 2011-2022 走看看