如何在线程“main”java.util.ConcurrentModificationException中修复Exception

时间:2013-10-01 11:03:27

标签: java

我有2个HashMap<Integer,Point3D>个对象名称为positiveCoOrdinate and negativeCoOrdinates

我正在检查PositiveCoOrdinates以下条件。如果它满足添加到negativeCoOrdinates并从positiveCoOrdinates删除的相应点。

  HashMap<Integer, Point3d> positiveCoOrdinates=duelList.get(1);
  HashMap<Integer, Point3d> negativecoOrdinates=duelList.get(2);
  //condition
  Set<Integer> set=positiveCoOrdinates.keySet();
    for (Integer pointIndex : set) {
        Point3d coOrdinate=positiveCoOrdinates.get(pointIndex);
        if (coOrdinate.x>xMaxValue || coOrdinate.y>yMaxValue || coOrdinate.z>zMaxValue) {
            negativecoOrdinates.put(pointIndex, coOrdinate);
            positiveCoOrdinates.remove(pointIndex);
        }
    }

添加时,删除时间我收到以下错误。

 Exception in thread "main" java.util.ConcurrentModificationException
at java.util.HashMap$HashIterator.nextEntry(Unknown Source)
at java.util.HashMap$KeyIterator.next(Unknown Source)
at PlaneCoOrdinates.CoordinatesFiltering.Integration(CoordinatesFiltering.java:167)
at PlaneCoOrdinates.CoordinatesFiltering.main(CoordinatesFiltering.java:179)

对于我的测试,我在System.out.println(coOrdinate.x); condition.it中提到If语句。工作正常。

如果我在If条件中添加2行(我上面提到的内容),则会抛出错误。

我该如何解决这个问题。

感谢。

4 个答案:

答案 0 :(得分:11)

最简单的方法是复制keySet:

  Set<Integer> set= new HashSet<Integer>(positiveCoOrdinates.keySet());

出现此问题是因为您在使用遍历密钥的positiveCoOrdinates时修改了Iterator

您还可以重构代码并在条目集上使用迭代器。这将是一种更好的方法。

Set<Entry<Integer, Point3d>> entrySet = positiveCoOrdinates.entrySet();

    for (Iterator<Entry<Integer, Point3d>> iterator = entrySet.iterator(); iterator.hasNext();) {
        Entry<Integer, Point3d> entry = iterator.next();
        Point3d coOrdinate = entry.getValue();
        if (coOrdinate.x > xMaxValue || coOrdinate.y > yMaxValue
                || coOrdinate.z > zMaxValue) {
            Integer pointIndex = entry.getKey();
            negativecoOrdinates.put(pointIndex, coOrdinate);
            iterator.remove();
        }
    }

答案 1 :(得分:2)

使用增强型remove()循环时,您无法for-each来自迭代集合。 for-each循环隐式使用Iterator<Integer>JavaDoc明确指出

  

此类的所有“集合视图”返回的迭代器   方法“是快速失败的:如果地图在结构上被修改了   创建迭代器之后的时间,除了通过之外的任何方式   迭代器自己的remove()方法,迭代器会抛出一个   ConcurrentModificationException。因此,面对并发   修改,迭代器快速而干净地失败,而不是   在不确定的时间冒着任意的,非确定性的行为   在将来。

for-each循环在内部创建一个迭代器并使用它遍历该集合。然后你改变集合的结构......并且迭代器必须失败。问题是您无法访问迭代器的方法,因此必须明确使用Iterator<Integer>。生成的遍历字节码将是相同的,唯一的区别是您可以在遍历时从列表中删除元素。

Set<Integer> set = positiveCoOrdinates.keySet();
for (Iterator<Integer> iterator = set.iterator(); iterator.hasNext(); ) {
    Integer pointIndex = iterator.next();
    Point3d coOrdinate = positiveCoOrdinates.get(pointIndex);
    if (coOrdinate.x>xMaxValue || coOrdinate.y>yMaxValue || coOrdinate.z>zMaxValue) {
        negativecoOrdinates.put(pointIndex, coOrdinate);
        iterator.remove(pointIndex);    // this line changed!
    }
}

如果您不熟悉迭代器及其功能,请参阅the Oracle tutorial on Collections

  

Iterator是一个允许您遍历a的对象   如果,有选择地收集和从集合中删除元素   期望。您可以通过调用Iterator来获取集合的iterator()   方法

     

请注意,Iterator.remove()是唯一可以安全修改的方法   迭代期间的集合;如果是,则行为未指定   在迭代时,底层集合以任何其他方式进行修改   正在进行中。

     

在需要时使用Iterator代替for-each构造:

     
      
  • 删除当前元素。 for-each构造隐藏了迭代器,因此您无法调用remove()。因此,for-each   构造不可用于过滤。
  •   

答案 2 :(得分:0)

如果要在运行时修改集合,则需要使用Iterator而不是增强for循环。因为增强的for循环仅提供只读功能。以下是Iterator示例:

Iterator<Entity> iterator = collection.Iterator();
while(iterator.hasNext()){
  //DO Your Stuff
  iterator.remove(); // this function call remove the element from collection at run time
}

答案 3 :(得分:0)

正如René所指出的,这个非常常见的问题的原因是在一个集合被另一个集合读取时对它进行并发修改。

您可以使用ConcurrentHashMapCopyOnWriteArrayLit之类的集合,但请注意这些方法可能只是expensive和简单的更换器,以便在迭代时消除对同一集合的读取将解决此类问题。