为什么我不能在线程中正确地删除JDOM节点?

时间:2014-07-28 22:41:08

标签: java jdom

我知道为什么我的代码会引发一个问题,即xml文件中的某些节点实际上并没有被删除。

我需要维护一个java应用程序,这个应用程序的目的是比较两个PDF文件。应用程序从xml文件中的两个服务器中提取数据;然后它删除两个xml文件中的一些节点以忽略某些部分,最后应用程序将两个被操纵的xml文件发送回两个服务器以获取PDF文件,并拉取PDF文件以字节比较。该应用程序使用JDOM API来解析和操作xml文件。

这是我的代码:

/**
 * Ignores all Changes of this Form
 */
@SuppressWarnings("rawtypes")
protected void ignoreChangesSection() {
    Element formNumberElement=retrieveElement(getSiapXmlDocument(), FORM_REQUESTED_ELEMENT).getChild(FORM_NUMBER_ELEMENT);
    String formNumber=formNumberElement.getText();



    /*retrieveElement(getSiapXmlDocument(), DATA_ELEMENT).getChild(APPROACH_RESOURCE_INSTANCE_ELEMENT).
                                          getChild(APPROACH_ELEMENT).removeChildren(CHANGES_ELEMENT);*/

    Element approachElement = 
        retrieveElement(getSiapXmlDocument(), DATA_ELEMENT).getChild(APPROACH_RESOURCE_INSTANCE_ELEMENT).
                                      getChild(APPROACH_ELEMENT);

    List changeReasonList1 = approachElement.getChildren(CHANGES_ELEMENT);

    if (changeReasonList1!=null)
    {           
        for (int i=0; i<changeReasonList1.size(); i++)
        {
            Element chngRsn = (Element)changeReasonList1.get(i);

            String ind = chngRsn.getChildText(REASON_CHANGE_IND);

            if (ind != null && !"".equals(ind))
            {

                if (ind.equals(CHANGE) || (formNumber.equals("8260-7A") && ind.equals(CHANGE_FORM_7A)))
                {

                    chngRsn.detach();
                }
            }
        }
    }

}

在approachElement下有9个节点与“ChangeReasons”完全相同,是否应该删除它们取决于“REASON_CHANGE_IND”属性的值,所以我使用循环来完成这项工作。但是,通常不会删除一个应该删除的节点。这个问题不时发生,而不是每次都发生。我怀疑它与线程有关。由于有2个线程正在执行相同的工作,因此从不同的xml文件中删除相同的节点。但是,该对象是在每个线程内创建的,其类具有此方法。所以我想没有线程会对其他线程有所了解。如果我在代码中使用removeChilren,我没有这个问题。但它会删除所有节点,而不是我要删除的某些节点。任何人都可以给我一个暗示,为什么会出现这个问题和任何解决方案?

非常感谢!

萨姆

1 个答案:

答案 0 :(得分:0)

分离节点时,还要将列表的大小减一......考虑一个包含四个节点A,B,B,C的列表。

您想要删除/分离B节点

在你的循环中,你迭代并从0开始,然后移到1 ....你删除节点1.但是,现在你的列表是:A,B,C,你的索引是1.然后你再次循环,你的索引是2,它指向C,然后你退出循环。

您不会删除第二个节点B.

这是一个常见的计算错误,在循环遍历列表时,您无法轻松地从列表中删除节点。

Java提供了一个Iterator,JDOM也实现了它,这将是您正确的选择......现在,您应该使用JDOM 2.x而不是JDOM 1.x. 2会更容易,但是,即使使用JDOM 1 ....使用迭代器:

for (Iterator it = changeReasonList1.iterator(); it.hasNext();) {
    Element chngRsn = (Element)it.next();
    if (various reasons) {
        // detach the element:
        it.remove();
    }
}

编辑,从评论中更新:

两个关键点是:

  1. emt.detach()从其父级中移除emt
  2. approachElement.getChildren(...)返回&#39;直播&#39;名单。对列表的更改将反映在元素中,反之亦然。
  3. 因此,当您遍历luve列表并将emt作为索引get(...)时,您将获得一个子元素。当您分离该子项时,您将其与父项断开连接,从而也将该子项从approachElement列表中删除。该列表缩小了一个。

    所以,你的循环:

    for (int i=0; i<changeReasonList1.size(); i++)
    

    当你这样做时:

    chngRsn.detach();
    

    实质上你也在做:

    changeReasonList1.remove(i);
    

    这是关键点.....你在列表循环中有效地删除(i)。如果你把JDOM从quation中拿出来,并且情况更简单:

    List<Integer> data = new ArrayList<>();
    data.add(1);
    data.add(2);
    data.add(3);
    
    for (int i = 0; i < data.size(); i++) {
        System.out.println("before: " + data.get(i));
        if (i == 1) {
            data.remove(i);
        }
        System.out.println("after: " + data.get(i));
    }
    

    使用上面的代码,它将起作用,并打印:

    before: 1
    after: 1
    before: 2
    after: 3
    

    然后退出.....

    因此,它永远不会测试&#39;之前的值3因为,删除2,我们缩小列表,对于最后一个循环,大小为2,并且索引在循环中递增为2,因此循环是终止。

    Iterator避免了这一点。