【问题标题】:Why am I not getting a java.util.ConcurrentModificationException in this example?为什么在此示例中我没有收到 java.util.ConcurrentModificationException?
【发布时间】:2012-01-01 15:11:24
【问题描述】:

注意:我知道Iterator#remove() 方法。

在下面的代码示例中,我不明白为什么main 方法中的List.remove 抛出ConcurrentModificationException,但remove 方法中不是

public class RemoveListElementDemo {    
    private static final List<Integer> integerList;

    static {
        integerList = new ArrayList<Integer>();
        integerList.add(1);
        integerList.add(2);
        integerList.add(3);
    }

    public static void remove(Integer toRemove) {
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }

    public static void main(String... args) {                
        remove(Integer.valueOf(2));

        Integer toRemove = Integer.valueOf(3);
        for(Integer integer : integerList) {
            if(integer.equals(toRemove)) {                
                integerList.remove(integer);
            }
        }
    }
}

【问题讨论】:

  • 在迭代列表时从列表中删除元素的唯一安全方法是使用Iterator#remove()。你为什么要这样做?
  • @MattBall:我只是想看看这里可能是什么原因。因为,这两种方法中的“增强型 for 循环”是相同的,但一种抛出 ConcurrentModificationException 而另一种则没有。
  • 您删除的元素有所不同。在该方法中,您删除了“中间元素”。主要是删除最后一个。如果您交换数字,您的方法中会出现异常。仍然不确定为什么会这样。
  • 我遇到了类似的问题,当我的循环迭代了一个在我删除循环中的项目后不存在的位置时。我只是通过在循环中添加return; 来解决这个问题。
  • 在 java8 Android 上,删除除最后一个以外的元素会调用 ConcurrentModificationException。所以对于你的情况,删除函数会得到一个与你之前观察到的相反的异常。

标签: java list concurrentmodification foreach


【解决方案1】:

原因如下: 正如 Javadoc 中所说:

这个类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在任何结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的删除或添加方法,迭代器会抛出一个 ConcurrentModificationException。

这项检查是在迭代器的next() 方法中完成的(正如您在堆栈跟踪中看到的那样)。但是只有当hasNext() 传递为真时,我们才会到达next() 方法,这是由 for each 调用以检查是否满足边界的方法。在你的 remove 方法中,当hasNext() 检查它是否需要返回另一个元素时,它会看到它返回了两个元素,现在删除一个元素后,列表只包含两个元素。所以一切都很顺利,我们完成了迭代。不会发生并发修改检查,因为这是在从未调用过的 next() 方法中完成的。

接下来我们进入第二个循环。在我们删除第二个数字后,hasNext 方法将再次检查是否可以返回更多值。它已经返回了两个值,但列表现在只包含一个。但是这里的代码是:

public boolean hasNext() {
        return cursor != size();
}

1 != 2,所以我们继续使用 next() 方法,它现在意识到有人在弄乱列表并触发异常。

希望能解决您的问题。

总结

List.remove() 从列表中删除倒数第二个元素时不会抛出 ConcurrentModificationException

【讨论】:

  • @pushy:只回答似乎回答了问题的实际问题,并且解释得很好。我接受这个答案,也+1。谢谢。
【解决方案2】:

处理它的一种方法是从Collection(不是集合本身)的副本中删除某些内容(如果适用)。 Clone原始收藏它通过Constructor制作副本。

检测到并发的方法可能会抛出此异常 在不允许修改时修改对象。

对于您的具体情况,首先,考虑到您打算修改列表过去的声明,我认为final 不是一种方法

private static final List<Integer> integerList;

还可以考虑修改副本而不是原始列表。

List<Integer> copy = new ArrayList<Integer>(integerList);

for(Integer integer : integerList) {
    if(integer.equals(remove)) {                
        copy.remove(integer);
    }
}

【讨论】:

    【解决方案3】:

    前向/迭代器方法在删除项目时不起作用。您可以删除元素而不会出错,但是当您尝试访问已删除的项目时会出现运行时错误。您不能使用迭代器,因为 pushy 表明它会导致 ConcurrentModificationException,因此请改用常规 for 循环,但要后退一步。

    List<Integer> integerList;
    integerList = new ArrayList<Integer>();
    integerList.add(1);
    integerList.add(2);
    integerList.add(3);
    
    int size= integerList.size();
    
    //Item to remove
    Integer remove = Integer.valueOf(3);
    

    解决方案:

    如果要删除列表元素,则以相反的顺序遍历数组。只需向后浏览列表,您就可以避免访问已删除的项目,从而消除异常。

    //To remove items from the list, start from the end and go backwards through the arrayList
    //This way if we remove one from the beginning as we go through, then we will avoid getting a runtime error
    //for java.lang.IndexOutOfBoundsException or java.util.ConcurrentModificationException as when we used the iterator
    for (int i=size-1; i> -1; i--) {
        if (integerList.get(i).equals(remove) ) {
            integerList.remove(i);
        }
    }
    

    【讨论】:

    • 好主意!
    【解决方案4】:

    这个 sn-p 将总是抛出 ConcurrentModificationException。

    规则是“在使用迭代器迭代它时,你不能修改(从列表中添加或删除元素)(当你使用 for-each 循环时会发生这种情况)”。

    JavaDocs:

    这个类的 iterator 和 listIterator 方法返回的迭代器是快速失败的:如果在迭代器创建后的任何时候列表在结构上被修改,除了通过迭代器自己的 remove 或 add 方法之外的任何方式,迭代器将抛出 ConcurrentModificationException。

    因此,如果您想修改列表(或一般的任何集合),请使用迭代器,因为它会知道修改,因此将正确处理这些修改。

    希望这会有所帮助。

    【讨论】:

    • OP 明确指出其中一个循环不会引发异常,而疑问是发生这种情况的原因。
    • 你所说的“疑问”是什么意思?
    【解决方案5】:

    我遇到了同样的问题,但如果我将 en 元素添加到迭代列表中。 我是这样弄的

    public static void remove(Integer remove) {
        for(int i=0; i<integerList.size(); i++) {
            //here is maybe fine to deal with integerList.get(i)==null
            if(integerList.get(i).equals(remove)) {                
                integerList.remove(i);
            }
        }
    }
    

    现在一切正常,因为您没有在列表上创建任何迭代器,而是“手动”迭代它。并且条件i &lt; integerList.size() 永远不会欺骗您,因为当您将某些内容删除/添加到列表减量/增量的列表大小时..

    希望对我有帮助,这就是解决方案。

    【讨论】:

    • 这不是真的!证据:运行此 sn-p 以查看结果: public static void main(String... args) { List listOfBooks = new ArrayList(); listOfBooks.add("代码完成"); listOfBooks.add("代码 22"); listOfBooks.add("22 有效"); listOfBooks.add("Netbeans 33"); System.err.println("删除前:" + listOfBooks); for (int index = 0; index
    【解决方案6】:

    如果您使用写时复制集合,它将起作用;但是,当您使用 list.iterator() 时,返回的 Iterator 将始终引用元素的集合,就像它在(如下所示)时一样 list.iterator() 被调用,即使另一个线程修改了集合。任何 在基于写时复制的迭代器或 ListIterator 上调用的变异方法 (如添加、设置或删除)将抛出 UnsupportedOperationException。

    import java.util.List;
    import java.util.concurrent.CopyOnWriteArrayList;
    
    public class RemoveListElementDemo {    
        private static final List<Integer> integerList;
    
        static {
            integerList = new CopyOnWriteArrayList<>();
            integerList.add(1);
            integerList.add(2);
            integerList.add(3);
        }
    
        public static void remove(Integer remove) {
            for(Integer integer : integerList) {
                if(integer.equals(remove)) {                
                    integerList.remove(integer);
                }
            }
        }
    
        public static void main(String... args) {                
            remove(Integer.valueOf(2));
    
            Integer remove = Integer.valueOf(3);
            for(Integer integer : integerList) {
                if(integer.equals(remove)) {                
                    integerList.remove(integer);
                }
            }
        }
    }
    

    【讨论】:

      【解决方案7】:

      这在 Java 1.6 上运行良好

      ~ % javac RemoveListElementDemo.java
      ~ % java RemoveListElementDemo
      ~ % cat RemoveListElementDemo.java

      import java.util.*;
      public class RemoveListElementDemo {    
          private static final List<Integer> integerList;
      
          static {
              integerList = new ArrayList<Integer>();
              integerList.add(1);
              integerList.add(2);
              integerList.add(3);
          }
      
          public static void remove(Integer remove) {
              for(Integer integer : integerList) {
                  if(integer.equals(remove)) {                
                      integerList.remove(integer);
                  }
              }
          }
      
          public static void main(String... args) {                
              remove(Integer.valueOf(2));
      
              Integer remove = Integer.valueOf(3);
              for(Integer integer : integerList) {
                  if(integer.equals(remove)) {                
                      integerList.remove(integer);
                  }
              }
          }
      }
      

      ~%

      【讨论】:

      • 抱歉打错了这在 Java 1.6 上运行良好
      • 嗯...可能你有不同的实现。但根据规范,它应该这样做,IMO。看看@Pushy 的回答。
      • 不幸的是,id 在 java 1.8 上没有
      【解决方案8】:

      就我而言,我是这样做的:

      int cursor = 0;
      do {
          if (integer.equals(remove))
              integerList.remove(cursor);
          else cursor++;
      } while (cursor != integerList.size());
      

      【讨论】:

        【解决方案9】:

        将迭代器for each改为for loop即可解决。

        原因是:

        这个类的迭代器和listIterator返回的迭代器 方法是快速失败的:如果列表在任何结构上被修改 迭代器创建后的时间,除了通过 迭代器自己的删除或添加方法,迭代器会抛出一个 ConcurrentModificationException。

        --参考的 Java 文档。

        【讨论】:

          【解决方案10】:

          检查你的代码人......

          在 main 方法中,您试图删除不存在的第 4 个元素,因此会出现错误。 在 remove() 方法中,您尝试删除存在的第三个元素,因此没有错误。

          【讨论】:

          • 你错了:数字23 不是列表的索引,而是元素。两个删除逻辑都针对列表元素检查equals,而不是元素的索引。此外,如果它与索引相关,它将是IndexOutOfBoundsException,而不是ConcurrentModificationException
          猜你喜欢
          • 1970-01-01
          • 1970-01-01
          • 2016-06-09
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          相关资源
          最近更新 更多