[英]Avoiding ConcurrentModificationException in dijkstra's algorithm
[英]ConcurrentModificationException while trying to implement Dijkstra algorithm
我正在嘗試在迷宮中實現Dijkstra的最短路徑算法。 ( http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm )
我有兩個HashSets,一個用於訪問,一個用於未訪問的字段。 一旦所有鄰居通過算法訪問了一個字段,我想將其放入訪問的地圖中並將其從未訪問的地圖中刪除。
但是,當我嘗試運行算法時,我在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()
來自API :
這個類的迭代器方法返回的迭代器是快速失敗的:如果在創建迭代器之后的任何時候修改了set,除了通過迭代器自己的remove方法之外,Iterator拋出ConcurrentModificationException。 因此,在並發修改的情況下,迭代器快速而干凈地失敗,而不是在未來的未確定時間冒着任意的,非確定性行為的風險。
這意味着如果要在迭代時修改集合,則必須使用iterator.remove()方法。 不要使用for循環,嘗試這樣的事情:
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);
如果您的集合是List類型,那么您可以通過調用listIterator()來獲取ListIterator 。 ListIterator通過添加允許雙向遍歷列表的方法來擴充Iterator,並允許通過添加和刪除項目或替換當前項目來修改集合。
在java中使用“for each”循環時,實際上你正在使用Iterator。 請參閱哪個更有效,for-each循環或迭代器? 。
由於您在不使用迭代器的add或remove方法的情況下修改底層集合(remove),因此您收到ConcurrentModificationException
因為Java集合是快速失敗的 :
如果迭代器在以下兩個條件之一中拋出ConcurrentModificationException,則認為迭代器是快速失敗的 :
在多線程處理中:如果一個線程正在嘗試修改Collection而另一個線程正在迭代它。
在單線程或多線程處理中:如果在創建Iterator之后,可以通過除Iterator自己的remove或add方法之外的任何方法隨時修改容器。
因此,您必須顯式使用Iterator,在Iterator而不是集合上調用remove。
一般提示:在許多情況下,可以通過轉換模式來避免ConcurrentModificationException
for (Element element : collection)
{
if (someCondition) collection.remove(element); // Causes Exception!
}
變成一種模式
Set<Element> toRemove = new HashSet<Element>(); // Or a list
for (Element element : collection)
{
if (someCondition) toRemove.add(element);
}
collection.removeAll(toRemove);
與明確處理Iterator相比,這通常更方便,更容易理解。 它也可能適用於您的情況(但誠然,並未完全遵循您的代碼進入最高嵌套深度)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.