我即将删除XML文档中的某些元素,使用如下代码:
NodeList nodes = ...;
for (int i = 0; i < nodes.getLength(); i++) {
Element e = (Element)nodes.item(i);
if (certain criteria involving Element e) {
e.getParentNode().removeChild(e);
}
}
这会干扰NodeList的正确遍历吗?用这种方法还有其他注意事项吗?如果这是完全错误的,那么正确的做法是什么?
答案 0 :(得分:11)
因此,假设在遍历NodeList时删除节点将导致NodeList更新以反映新的现实,我假设我的索引将变为无效,这将无效。
因此,似乎解决方案是在遍历期间跟踪要删除的元素,并在不再使用NodeList后将其全部删除。
NodeList nodes = ...;
Set<Element> targetElements = new HashSet<Element>();
for (int i = 0; i < nodes.getLength(); i++) {
Element e = (Element)nodes.item(i);
if (certain criteria involving Element e) {
targetElements.add(e);
}
}
for (Element e: targetElements) {
e.getParentNode().removeChild(e);
}
答案 1 :(得分:9)
循环时删除节点会导致不良结果,例如错过或重复的结果。这甚至不是同步和线程安全的问题,但是如果节点由循环本身修改。在这种情况下,大多数Java的Iterator都会抛出一个ConcurrentModificationException,这是NodeList没有考虑到的。
可以通过递减NodeList大小和同时递减iteraror指针来修复它。只有当我们为每个循环迭代进行一次删除操作时,才能使用此解决方案。
NodeList nodes = ...;
for (int i = nodes.getLength() - 1; i >= 0; i--) {
Element e = (Element)nodes.item(i);
if (certain criteria involving Element e) {
e.getParentNode().removeChild(e);
}
}
答案 2 :(得分:7)
根据DOM规范,调用 node.getElementsByTagName(“...”)的结果应该是“实时”,即对DOM树进行的任何修改将反映在 NodeList 对象中。那么,对于符合要求的实现,那就是......
中的NodeList和NamedNodeMap对象 DOM是活的;也就是说,改变了 底层文档结构是 反映在所有相关的NodeList和 NamedNodeMap对象。
因此,当您修改树结构时,符合标准的实现将更改 NodeList 以反映这些更改。
答案 3 :(得分:1)
Practical XML库现在包含NodeListIterator,它包装NodeList并提供完整的Iterator支持(这似乎是比发布我们在评论中讨论的代码更好的选择)。如果您不想使用完整的库,请随意复制该类:http://practicalxml.svn.sourceforge.net/viewvc/practicalxml/trunk/src/main/java/net/sf/practicalxml/util/NodeListIterator.java?revision=125&view=markup
答案 4 :(得分:0)
根据DOM Level 3 Core规范,
调用方法node.getElementsByTagName("...")
的结果将是对“ 实时 ”NodeList
类型的引用。
DOM中的NodeList和NamedNodeMap对象是实时的;也就是说,对底层文档结构的更改将反映在所有相关的NodeList和NamedNodeMap对象中。 ...更改会自动反映在NodeList中,而无需用户的进一步操作。
1.1.1 The DOM Structure Model, para. 2
JavaSE 7符合DOM Level 3规范:它实现 live NodeList接口并将其定义为类型;它在Interface Element上定义并公开getElementsByTagName
方法,该方法返回 实时 NodeList
类型。
W3C - Document Object Model (DOM) Level 3 Core Specification - getElementsByTagName
答案 5 :(得分:0)
旧帖子,但没有标记为答案。我的方法是从最后迭代,即
for (int i = nodes.getLength() - 1; i >= 0; i--) {
// do processing, and then
e.getParentNode().removeChild(e);
}
使用此功能,您无需担心删除时NodeList会变短。
答案 6 :(得分:0)
如前所述,删除元素会减小列表的大小,但计数器仍在增加(i ++):
[element 1] <- Delete
[element 2]
[element 3]
[element 4]
[element 5]
[element 2]
[element 3] <- Delete
[element 4]
[element 5]
--
[element 2]
[element 4]
[element 5] <- Delete
--
--
[element 2]
[element 4]
--
--
--
我认为,最简单的解决方案是在循环中删除i ++部分,并在未删除迭代元素时根据需要执行。
NodeList nodes = ...;
for (int i = 0; i < nodes.getLength();) {
Element e = (Element)nodes.item(i);
if (certain criteria involving Element e) {
e.getParentNode().removeChild(e);
} else {
i++;
}
}
删除迭代元素后,指针停留在同一位置。列表会自行移动。
[element 1] <- Delete
[element 2]
[element 3]
[element 4]
[element 5]
[element 2] <- Leave
[element 3]
[element 4]
[element 5]
--
[element 2]
[element 3] <- Leave
[element 4]
[element 5]
--
[element 2]
[element 3]
[element 4] <- Delete
[element 5]
--
[element 2]
[element 3]
[element 5] <- Delete
--
--
[element 2]
[element 3]
--
--
--