我有一个使用Java HashMap使用邻接列表实现的有向图。 Graph类只存储这样的指针:
HashMap<Node<V>, List<Edge<V>>> graph;
我正在尝试编写一种可以执行图形转置的方法(通过副作用)。这是代码:
/**
* Helper method for connection test
*/
public void reverseDirection(){
for(Node<V> v : getNodes()){
for(Edge<V> e : getOutEdges(v)){
Node<V> target = e.getTarget();
int weight = e.getWeight();
graph.get(v).remove(e);
graph.get(target).add(new Edge<V>(v, weight));
}
}
}
在执行某些测试时,我得到了这个:
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.LinkedList$ListItr.checkForComodification(LinkedList.java:953)
at java.util.LinkedList$ListItr.next(LinkedList.java:886)
at esercitazione9.Graph.reverseDirection(Graph.java:71)
at esercitazione9.GraphUtil.fortementeConnesso(GraphUtil.java:126)
at esercitazione9.GraphUtil.main(GraphUtil.java:194)
Javadoc说这个异常并不总是表明对象已被同时修改。即使线程在迭代集合时直接修改集合,也可能发生这种情况。
这正是我的情况,但我没有想法解决它。还有另一种方法可以在没有迭代器收集干扰的情况下反转所有边缘方向吗?注意:计算成本不能高于O(n + m)。
答案 0 :(得分:1)
除了使用迭代器的remove()
方法之外,你不能以任何其他方式从集合中删除项目(好吧,除非它是ConcurrentHashMap
,我现在想不到其他的例外情况)。这个问题有两种规范的解决方案:
重写您的循环以使用明确的Iterator
并拨打remove
而不是graph.get(v).remove(e);
创建一个单独的集合,用于保存要从迭代的集合中删除的项目(或者,要保留的项目),并在实际迭代后执行。
当你明确要求“不是1”时,我相信这是唯一的选择。如果存储要删除的项目,则计算成本不应增加,因为分配和插入的数量不能大于O(n + m)(n个集合,m个删除的边缘总数)。请记住,如果您的图表包含循环,则必须特别小心。
答案 1 :(得分:0)
确定。我只是按照建议修改了代码:
public void reverseDirection(){
Collection<Edge<V>> removed = new LinkedList<Edge<V>>();
for(Node<V> v : getNodes()){
for(Edge<V> e : getOutEdges(v)){
Node<V> target = e.getTarget();
int weight = e.getWeight();
removed.add(e);
graph.get(target).add(new Edge<V>(v, weight));
}
graph.get(v).removeAll(removed);
}
}
我认为现在算法的逻辑存在一些问题,因为它没有返回预期的结果。我稍后会发布固定代码。