【问题标题】:Java: How do handle ConcurrentModificationException in this particular situation?Java:在这种特殊情况下如何处理 ConcurrentModificationException?
【发布时间】:2014-12-10 16:54:30
【问题描述】:

在我发布这个问题之前,我阅读了here 的帖子。但是对于这个特殊的问题,我真的不知道我该如何应用同样的理念?

在以下问题中,我的变量graph 具有以下结构:HashMap<Integer, ArrayList<Integer>>。 而uv 的类型为int。基本上,我想删除键值为i 的arraylists 中的值v,其中i 出现在v 的arraylist 中。

所以,如果整数i出现在v的arraylist中,我们在图中得到i的arraylist并从中删除值v

这听起来很复杂。但是我现在被这个 ConcurrentModificationException 困了一段时间。

public int random_contraction_algo(){
    while(graph.size() > 2){
        int u = select_remaining_edges_at_random(new ArrayList<Integer> (graph.keySet()));
        int v = select_remaining_edges_at_random(graph.get(u));      
        merge(u,v);        
    }
    return -1;
}

// select a pair of vertices from an edge
public int select_remaining_edges_at_random(ArrayList<Integer> vertices){
    int index = (int)(Math.random() * (vertices.size()));
    return vertices.get(index);
}

public void merge(int u, int v){

    graph.get(u).addAll(graph.get(v));

    // remove self-loops
    graph.get(u).removeAll(Collections.singleton(u));
    graph.get(u).removeAll(Collections.singleton(v));

    // make sure all the edges of v are connected to u instead v
    for(Iterator<Integer> iterator = graph.get(v).iterator(); iterator.hasNext();){
        Integer i = iterator.next();
        graph.get(i).remove((Integer) v);
        graph.get(i).add(u);

    }
    // remove key
      graph.remove(v);
}

更新:非常感谢您的回答。但是,我意识到我忘了告诉你们我的代码中有一个外循环。

我尝试实施您的解决方案,但 ConcurrentModificationException 仍然发生。

这些是错误消息:

Exception in thread "main" java.util.ConcurrentModificationException
    at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:886)
    at java.util.ArrayList$Itr.next(ArrayList.java:836)
    at HashMapOfArrayList.merge(ArraylistOfArrayList.java:96)
    at HashMapOfArrayList.random_contraction_algo(ArraylistOfArrayList.java:69)
    at RunAlgo.main(ArraylistOfArrayList.java:111)

【问题讨论】:

  • 我认为这不会是一个问题,除非 i == v,但是你可以使用标准的迭代器方法。
  • 你能给我举个例子吗?
  • 在循环中修改其他列表不会有问题。如果您仍然遇到以下两种解决方案中的任何一种异常,这是由您未包含在帖子中的某些代码引起的。 @Ordous 的解决方案也可能会失败,如果您将相同的列表存储在地图中的不同键下。我的对此免疫(但效率稍低,因为它涉及对列表的一次添加扫描)。
  • 我将再次编辑这个问题,因为我想向你们展示更多涉及外循环的代码。请参阅更新
  • @mynameisJEFF 您是否有可能给出一个失败的小数据示例?像 2-3 个带边的节点?我已经尝试了 3 个节点,每个节点都相互连接,效果很好(如果没有明确的自循环删除,则会抛出异常)

标签: java exception arraylist


【解决方案1】:

问题在于,当v == i 时,您正在修改与迭代相同的列表。

一个简单的解决方案是防范这种情况:

for(Iterator<Integer> it = graph.get(v).iterator(); it.hasNext();) {
    Integer i = it.next();
    if (i == v) {
        it.remove();
    } else {
        graph.get(i).remove((Integer) v);
    }
}

【讨论】:

  • 此代码也可能导致并发修改异常,因为您正在使用迭代器访问 Map。
  • @markbernard 访问没问题,修改不行。并且地图没有在此处或问题中修改。
  • 这不起作用。因为 graph.get(i) 是 hashmap 中的另一个 arraylist,如果我尝试从 arraylist i 中删除一个项目,那么我正在修改映射。
  • @mynameisJEFF 如果您正在修改数组列表,那么您正在修改的是数组列表,而不是地图。除非有一个您没有显示的外部迭代并且再次涉及列表 - 这应该没问题。
  • 还有另一个外部 while 循环,但我只执行如下操作:graph.remove(v)。那么我可能需要发布一个新问题。
【解决方案2】:

从循环前的列表中显式删除vgraph.get(v).remove(v)

List<Integer> list = graph.get(v);
list.remove(v);
for(Integer i: list){
   graph.get(i).remove(v)
}

【讨论】:

    【解决方案3】:

    我可能误解了你。但是如果你提

    所以,如果整数 i 出现在 v 的 arraylist 中,我们就得到图中 i 的 arraylist 并从中移除值 v。

    这是你想做的吗?

    HashMap<Integer, ArrayList<Integer>> graph = new HashMap<Integer, ArrayList<Integer>>();
    for (int i = 0; i < 100; i++) {
        graph.put(i, new ArrayList<Integer>());
    }
    
    int i = 0; // change value to whatever
    int v = 0; // change value to whatever
    if (graph.get(v).contains(i)) {
        graph.get(i).remove(new Integer(v));
    }
    

    如果不是,您可以尝试准确地解释您需要什么。

    编辑:

    我认为这应该可行:

    HashMap<Integer, ArrayList<Integer>> graph = new HashMap<Integer, ArrayList<Integer>>();
    for (int i = 0; i < 100; i++) {
        graph.put(i, new ArrayList<Integer>());
    }
    
    int v = 0; // change value to whatever
    int u = 0;
    ArrayList<Integer> list = new ArrayList<Integer>(graph.get(v));
    for(Integer i : list){
        if(graph.get(i).contains(v)){
            graph.get(i).remove(new Integer(v));
            graph.get(i).add(u);
        }
    }
    

    这会创建一个有问题的ArrayList 的副本,因此我们永远不会在地图本身上进行迭代,从而使对地图的编辑成为可能。

    告诉我它是否有效。

    【讨论】:

    • 是的。但现在我想做一个额外的步骤。看解释:有一个循环遍历v的arraylist的每个元素。对于v 的arraylist 中的每个元素i,我使用它作为键在同一个hashmap 中grep 其对应的arraylist。如果i 的arraylist 包含值v,我想删除它并在i 的arraylist 中添加一个新整数u。 (请注意,iv 永远不会相同)。
    • 我想我明白了,请稍等。
    • @mynameisJEFF 我编辑了答案(虽然还没有运行它)所以检查它是否适合你。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-05-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多