我想合并具有相同名称的节点,并将其子代添加在一起。
但是我有一个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;
        }
    }
}

最佳答案

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);
    }
}


这个循环是您代码中的问题。使用迭代器时,不能修改基础Collection。在这种情况下,您将在此循环中遍历“子级”,并在调用“ node.removeChildNode(child)”时从基础列表中删除项目。

一种解决方案是在迭代“子项”列表之前对其进行克隆。

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


这意味着您不再需要遍历该方法中稍后要编辑的列表。

您还可以创建另一个列表,以在完成对其进行迭代之后存储要删除的子节点。

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


最后,您可以使用'it.remove()'从基础Collection中删除元素。该方法的缺点是破坏封装。

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

07-24 09:47
查看更多