在树结构中合并节点时出现java.util.ConcurrentModificationException

时间:2014-08-15 06:49:33

标签: java exception tree

我想合并具有相同名称的节点,并将它们的子节点添加到一起。 但是我得到了一个java.util.ConcurrentModificationException异常:

Exception in thread "main" java.util.ConcurrentModificationException    
at java.util.ArrayList$Itr.checkForComodification(Unknown Source)   
at java.util.ArrayList$Itr.next(Unknown Source)     
at test.Test.mergeNode(Test.java:60)    
at test.Test.main(Test.java:43)

以下是来源。有人可以提供任何提示吗?任何建议都会受到欢迎。

public class Test {
    public static void main(String[] args) throws Exception {

        TreeLayoutNode root = new TreeLayoutNode();
        root.setName("Production");

        TreeLayoutNode node1 = new TreeLayoutNode();
        node1.setName("node1");

        TreeLayoutNode node2 = new TreeLayoutNode();
        node2.setName("node1");

        TreeLayoutNode child1 = new TreeLayoutNode();
        child1.setName("child1");

        TreeLayoutNode child2 = new TreeLayoutNode();
        child2.setName("child2");

        TreeLayoutNode child3 = new TreeLayoutNode();
        child3.setName("child3");

        root.addChildNode(node1);
        root.addChildNode(node2);

        node1.addChildNode(child1);
        node1.addChildNode(child2);

        node2.addChildNode(child1);
        node2.addChildNode(child3);

        HashMap<String, TreeLayoutNode> nodeMap = Maps.newHashMap();
        mergeNode(root, nodeMap);

    }

    /**
     * 
     * @param node
     */
    private static void mergeNode(TreeLayoutNode node, HashMap<String, TreeLayoutNode> nodeMap) {
        List<TreeLayoutNode> children = node.getChildren();

        if(CollectionUtils.isEmpty(children)){
            return;
        }

        Iterator<TreeLayoutNode> it = children.iterator();
        while(it.hasNext()){
            TreeLayoutNode child = it.next();
            if(nodeMap.containsKey(child.getName())){
                TreeLayoutNode duplicate = nodeMap.get(child.getName());
                List<TreeLayoutNode> childrenOfChild = child.getChildren();
                if(CollectionUtils.isNotEmpty(childrenOfChild)){
                    for(TreeLayoutNode single: childrenOfChild){
                        duplicate.addChildNode(single);
                    }
                    node.removeChildNode(child);
                    mergeNode(duplicate, nodeMap);
                }
            }else{
                nodeMap.put(child.getName(), child);
            }
        }
    }
}

public class TreeLayoutNode {

    private String name;

    private String parent;

    private Long capacity;

    private List<Proportion> proportions;

    private List<TreeLayoutNode> children;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getParent() {
        return parent;
    }

    public void setParent(String parent) {
        this.parent = parent;
    }

    public Long getCapacity() {
        return capacity;
    }

    public void setCapacity(Long capacity) {
        this.capacity = capacity;
    }

    public List<Proportion> getProportions() {
        return proportions;
    }

    public void setProportions(List<Proportion> proportions) {
        this.proportions = proportions;
    }

    public List<TreeLayoutNode> getChildren() {
        return children;
    }

    public void setChildren(List<TreeLayoutNode> children) {
        this.children = children;
    }

    public void addChildNode(TreeLayoutNode child) {
        if (children == null) {
            children = Lists.newArrayList();
        }

        child.setParent(this.getName());
        children.add(child);
    }

    public void removeChildNode(TreeLayoutNode child){
        children.remove(child);
    }

    public void addProportion(Proportion proportion) {
        if (proportions == null) {
            proportions = Lists.newArrayList();
        }
        proportions.add(proportion);
    }

    public int hashCode() {
        return name == null ? 0: name.hashCode();
    }

    public boolean equals(Object o) {
        if (o instanceof TreeLayoutNode) {
            TreeLayoutNode target = (TreeLayoutNode) o;
            if (this.name == null) {
                return target.getName() == null;
            } else {
                return this.name.equals(target.getName());
            }
        } else {
            return false;
        }
    }
}  

1 个答案:

答案 0 :(得分:2)

Iterator<TreeLayoutNode> it = children.iterator();
while(it.hasNext()){
    TreeLayoutNode child = it.next();
    if(nodeMap.containsKey(child.getName())){
        TreeLayoutNode duplicate = nodeMap.get(child.getName());
        List<TreeLayoutNode> childrenOfChild = child.getChildren();
        if(CollectionUtils.isNotEmpty(childrenOfChild)){
            for(TreeLayoutNode single: childrenOfChild){
                duplicate.addChildNode(single);
            }
            node.removeChildNode(child);
            mergeNode(duplicate, nodeMap);
        }
    }else{
        nodeMap.put(child.getName(), child);
    }
}

此循环是代码中的问题。使用Iterator时,无法修改基础Collection。在这种情况下,你正在迭代“孩子们”。在此循环中,当您调用“node.removeChildNode(child)&#39;”时,您将从基础列表中删除项目。

解决方案是克隆孩子们的生活。在迭代之前列出它。

List< TreeLayoutNode > children = node.getChildren().clone();

这意味着您不再迭代稍后在方法中编辑的列表。

您还可以创建另一个List来存储子节点,在迭代完毕后将其删除。

List< TreeLayoutNode > removedChildren = new LinkedList< >();
// Iterate over the elements, adding children to be removed to removedChildren
for( TreeLayoutNode child : removedChildren ) {
    node.removeChildNode( child );
}

最后你可以使用&#39; it.remove()&#39;从基础Collection中删除元素。该方法具有破坏封装的缺点。

// node.removeChildNode( child )
it.remove();