使用ArrayList和recursion避免java.util.ConcurrentModificationException

时间:2016-03-17 13:37:20

标签: java recursion arraylist

我试图从给定的边和根构造二叉树。我迭代所有行并将左子节点设置为给定节点(如果找到第二次给定节点则右子节点)。当设置子节点时,我从ArrayList中删除此元素,并在子节点上递归调用函数。

List<Integer[]> edges = new ArrayList<>();

此数组列表例如:

3 4
3 0
4 5
1 3
2 4 

和root是1。 我有类Node

public class Node {
public Node left;
public Node right;
public int key;

public Node(int k) {
    key = k;
    left = null;
    right = null;
}
...}

我构建树的功能是:

public static void edgeToNodes(Node root) {
    if (root == null) return;
    int count = 0;

    for (Iterator<Integer[]> iterator = edges.iterator(); iterator.hasNext(); ) {
        Integer[] edge = iterator.next();
        for (int i = 0; i < edge.length; i++) {
            if (edge[i] == root.key) {
                if (count == 0) {
                    Node left;
                    if (i == 0) {
                        left = new Node(edge[1]);
                        root.setLeft(left);
                    } else {
                        left = new Node(edge[0]);
                        root.setLeft(left);
                    }

                    iterator.remove();
                    count++;
                    edgeToNodes(left);
                } else {
                    Node right;
                    if (i == 0) {
                        right = new Node(edge[1]);
                        root.setRight(right);
                    } else {
                        right = new Node(edge[0]);
                        root.setRight(right);
                    }

                    iterator.remove();
                    edgeToNodes(right);
                }
            }
        }
    }
}

问题是它在java.util.ConcurrentModificationExceptionInteger[] edge = iterator.next();上为edgeToNodes(left);提供了帮助我如何避免此异常?

2 个答案:

答案 0 :(得分:1)

您正在迭代它时从列表中删除元素。

如果只通过一个迭代器来实现它,它会起作用。问题是您使用递归,因此创建几个修改相同列表的迭代器。这会导致ConcurrentModificationException

您可以以更迭代的方式构建节点。

  • 从根节点开始。
  • 维护要访问的节点列表(让我们称之为pendingNodes)。
  • 使用根节点初始化它。
  • 待处理节点列表不为空时:
    • pendingNodes
    • 的第一个元素
    • 找到潜在的孩子。
    • 如果有一些子节点尚未访问过
      • 将它们链接到父节点。
      • 将它们添加到pendingNodes
      • 列表中

以下是代码示例:

public class Problem {

  private List<Integer[]> edges;
  private LinkedList<Node> pendingNodes = new LinkedList<>();
  private Map<Integer, Node> nodeMap = new HashMap<>();
  private Set<Integer> visitedNodes = new HashSet<>();

  public Problem(List<Integer[]> edges) {
    this.edges = edges;
  }

  public void run(Integer root) {
    //Initialization
    Node rootNode = new Node(root);
    this.pendingNodes.add(rootNode);
    this.nodeMap.put(root, rootNode);

    while(!pendingNodes.isEmpty()) {
      Node parent = pendingNodes.poll();
      for (Integer[] edge : edges) {
        if(edge[0].equals(parent.getKey())) {
          link(parent, edge[1]);
        } else if (edge[1].equals(parent.getKey())) {
          link(parent, edge[0]);
        }
      }
      visitedNodes.add(parent.getKey());
    }
  }

  public void link(Node parent, Integer child) {
    if(!visitedNodes.contains(child)) {
      //get the corresponding node, create it if not exists
      Node childNode = nodeMap.get(child);
      if (childNode == null) {
        childNode = new Node(child);
        nodeMap.put(child, childNode);
        pendingNodes.add(childNode);
      }

      //Choose between left and right...
      if (parent.getLeft() == null) {
        parent.setLeft(childNode);
      } else {
        parent.setRight(childNode);
      }
    }
  }

  public static void main(String[] args) {

    //Build the input dataset
    List<Integer[]> edges = Arrays.asList(
        new Integer[]{3, 4},
        new Integer[]{3, 0},
        new Integer[]{4, 5},
        new Integer[]{1, 3},
        new Integer[]{2, 4}
    );

    Problem problem = new Problem(edges);
    problem.run(1);

    Map<Integer, Node> nodeMap = problem.nodeMap;
    for (Node node : nodeMap.values()) {
      System.out.println(node);
    }
  }

}

输出(我自定义Node#toString()):

0 => {null, null}
1 => {3, null}
2 => {null, null}
3 => {4, 0}
4 => {5, 2}
5 => {null, null}

答案 1 :(得分:0)

您遇到的问题是每次进行递归时,都会在同一列表上创建一个新的迭代器。迭代器(在某些时候)然后从该列表中删除项目,然后在递归中返回。然而,备份一步你的列表就丢失了一个元素,而你正在迭代它而没有它被迭代器本身删除(它被迭代器从递归线向下一步移除)。

=&GT; ConcurrentModificationException的