1

我有一个使用 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)。

4

2 回答 2

1

除了使用迭代器的方法之外,您不能以任何其他方式从您迭代的集合中删除项目remove()(好吧,除非它是ConcurrentHashMap,我目前无法想到其他例外)。这个问题有两种典型的解决方案:

  1. 重写循环以使用显式Iterators 并调用remove而不是graph.get(v).remove(e);

  2. 创建一个单独的集合来保存要从您迭代的集合中删除的项目(或者,要保留的项目),并在实际迭代之后执行此操作。

当您明确要求“非 1”时,我相信这是唯一的选择。如果您存储要删除的项目,则计算成本不应增加,因为分配和插入的数量不能大于 O(n+m)(n 个集合,总共 m 个删除的边)。请记住,如果您的图表包含循环,则必须特别小心。

于 2013-09-10T15:14:39.280 回答
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);
    }
}

我认为现在算法的逻辑存在一些问题,因为没有返回预期的结果。稍后我将发布固定代码。

于 2013-09-10T15:33:08.723 回答