Java ConcurrentModificationException在尝试实现Dijkstra算法时发生

Java ConcurrentModificationException在尝试实现Dijkstra算法时发生,java,hashset,concurrentmodification,Java,Hashset,Concurrentmodification,我试图在迷宫中实现Dijkstra的最短路径算法。 () 我得到了两个哈希集,一个用于访问的字段,另一个用于未访问的字段。 一旦一个字段被其所有邻居通过算法访问, 我想把它放在访问过的地图上,然后从未访问的地图上删除它 然而,当我尝试运行该算法时,我在Netbeans中得到一个ConcurrentModificationException 有趣的是——我已经读过这个问题,根据我的理解,这个问题来自于试图操纵/删除数据集中的数据,而不是通过迭代来解决。但是,我在集合的迭代中得到错误,即: for(

我试图在迷宫中实现Dijkstra的最短路径算法。 ()

我得到了两个哈希集,一个用于访问的字段,另一个用于未访问的字段。 一旦一个字段被其所有邻居通过算法访问, 我想把它放在访问过的地图上,然后从未访问的地图上删除它

然而,当我尝试运行该算法时,我在Netbeans中得到一个ConcurrentModificationException

有趣的是——我已经读过这个问题,根据我的理解,这个问题来自于试图操纵/删除数据集中的数据,而不是通过迭代来解决。但是,我在集合的迭代中得到错误,即:

for( Field unvisited : unvisitedFields ) {
因为我有时间问题,解决这个问题就足够了,但是如果我使用了错误的做法,我想知道解决这个问题的更好方法是什么。 下面方法的完整代码,unvisitedFields初始化为类变量,但具有与visitedFields相同的参数。它之所以成为类变量,是因为我在类的顶部填充了它,以及一个2D数组

public void calculcateSPath(Field curLocation , Field target ) {

        Set<Field> visitedFields = new HashSet<>();
        ArrayList<Field> shortestPath = new ArrayList<>();

        shortestPath.add( target );
        curLocation .setDistance( 0 );
        unvisitedFields.remove( curLocation  );
        visitedFields.add( curLocation  );

        // until all fields have a corresponding value to field, it continues searching.
        while( unvisitedFields.isEmpty() == false ) {

            // iterate through the Set containing all unvisited fields.
            for( Field unvisited : unvisitedFields ) {

                //iterate through the Set containing all visited fields.
                for( Field posNeighbor : visitedFields ) {

                    // if the unvisited field has a visited field as neighbor
                    if( unvisited.allNeighbors().containsValue( posNeighbor )) {

                        // check if the wall between them is down
                        if(( unvisited.getNeighbor( Direction.up ).equals( posNeighbor ) && posNeighbor.drawDown() == false )
                            || ( unvisited.getNeighbor( Direction.right ).equals( posNeighbor ) && unvisited.drawRight() == false )
                            || ( unvisited.getNeighbor( Direction.down ).equals( posNeighbor ) && unvisited.drawDown() == false )
                            || ( unvisited.getNeighbor( Direction.left ).equals( posNeighbor ) && posNeighbor.drawRight() == false )) {

                            visitedFields.add( posNeighbor );

                            // if so, check if the current distance is smaller than the previous distance.
                            if( unvisited.getDistance() > ( posNeighbor.getDistance()+1 ) ) {

                                // assign the new shorter distance and the connection point
                                unvisited.setDistance( posNeighbor.getDistance() + 1 );
                                unvisited.setVisitedNeighbors( 1 );
                                unvisited.setPrevious( posNeighbor );

                            }
                        // if not, add a count to the visited neighbors    
                        } else {

                            unvisited.setVisitedNeighbors( 1 );

                        }

                        //if all neighbors have been visited, remove the field from the unvisited list and add to visited.
                        if( unvisited.getVisitedNeighbors() == unvisited.allNeighbors().size() ) {

                            unvisitedFields.remove( unvisited );
                            visitedFields.add( posNeighbor );

                        }
                    }
                }
            }
        }

    } // ends calculateSPath()
public void calcultatespath(字段curLocation,字段目标){
Set visitedFields=new HashSet();
ArrayList shortestPath=新的ArrayList();
最短路径。添加(目标);
curLocation.setDistance(0);
未访问的字段。删除(卷曲定位);
visitedFields.add(curLocation);
//直到所有字段都有对应于字段的值,它才继续搜索。
while(unvisitedFields.isEmpty()==false){
//遍历包含所有未访问字段的集合。
对于(未访问的字段:未访问的字段){
//遍历包含所有已访问字段的集合。
用于(字段:已访问字段){
//如果未访问的字段有一个已访问的字段作为邻居
if(unvisited.allneighbor().containsValue(posNeighbor)){
//检查他们之间的墙是否倒了
if((unvisited.getNeighbor(Direction.up).equals(posNeighbor)&&posNeighbor.drawDown()==false)
||(unvisited.getNeighbor(Direction.right).equals(posNeighbor)&&unvisited.drawRight()==false)
||(unvisited.getNeighbor(Direction.down).equals(posNeighbor)&&unvisited.drawDown()==false)
||(unvisited.getNeighbor(Direction.left).equals(posNeighbor)和&posNeighbor.drawRight()==false)){
visitedFields.add(posNeighbor);
//如果是,请检查当前距离是否小于上一距离。
if(unvisited.getDistance()>(posNeighbor.getDistance()+1)){
//指定新的较短距离和连接点
unvisited.setDistance(posNeighbor.getDistance()+1);
未访问。设置访问邻居(1);
未访问。setPrevious(posNeighbor);
}
//如果没有,则向访问的邻居添加计数
}否则{
未访问。设置访问邻居(1);
}
//如果访问了所有邻居,请从未访问列表中删除该字段并添加到已访问。
if(unvisited.getVisitedNeights()==unvisited.allNeights().size()){
未访问字段。删除(未访问);
visitedFields.add(posNeighbor);
}
}
}
}
}
}//结束calculatePath()
在java中使用“for-each”循环时,实际上是在幕后使用迭代器。看

由于您在不使用迭代器的add或remove方法的情况下修改基础集合(remove),因此会得到一个
ConcurrentModificationException
,因为java集合很快就会失败:

如果迭代器抛出 以下两种情况下的ConcurrentModificationException 条件:

  • 在多线程处理中:如果一个线程试图修改 当另一个线程在其上迭代时

  • 在单线程或多线程处理中:如果在创建 对于迭代器,容器可以随时通过任何方法进行修改 而不是迭代器自己的remove或add方法

  • 因此,您必须显式使用迭代器,在迭代器上而不是在集合上调用remove。

    来自:

    此类的迭代器方法返回的迭代器是fail fast: 如果在创建迭代器后的任何时间修改集合,则在 除了通过迭代器自己的remove方法,迭代器 抛出ConcurrentModificationException。因此,面对 并发修改,迭代器快速、干净地失败, 而不是冒着一种随意的、不确定的行为的风险 未来时间未定

    这意味着,如果要在迭代集合时修改集合,则必须使用iterator.remove()方法。不要使用for each循环,请尝试以下方法:

    Collection items = ...
    Iterator itr = items.iterator();
    while(itr.hasNext()) {
      Object o = itr.next();
      boolean condition = ...
      if(condition) {
        itr.remove();
      }
    }
    
    Collection items = ...
    Collection itemsToRemove = ...
    for (Object item : items) {
      boolean condition = ...
      if (condition) {
        itemsToRemove.add(item);
      }
    }
    items.removeAll(itemsToRemove);
    
    或者,如果您可以(并且更愿意)在迭代完成后进行修改,您可以这样做:

    Collection items = ...
    Iterator itr = items.iterator();
    while(itr.hasNext()) {
      Object o = itr.next();
      boolean condition = ...
      if(condition) {
        itr.remove();
      }
    }
    
    Collection items = ...
    Collection itemsToRemove = ...
    for (Object item : items) {
      boolean condition = ...
      if (condition) {
        itemsToRemove.add(item);
      }
    }
    items.removeAll(itemsToRemove);
    

    如果集合的类型为,则可以通过调用。ListIterator通过添加允许双向遍历列表的方法来增强迭代器,并允许通过添加和删除项或替换当前项来修改集合。

    一般提示:在许多情况下,通过转换pat可以避免ConcurrentModificationException