遍历NodeList时删除DOM节点

我即将删除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的正确遍历吗? 用这种方法还有其他注意事项吗? 如果这是完全错误的,那么正确的做法是什么?

因此,鉴于在遍历NodeList时删除节点将导致NodeList更新以反映新的现实,我假设我的索引将变为无效,这将无效。

因此,似乎解决方案是在遍历期间跟踪要删除的元素,并在一旦不再使用NodeList之后将其全部删除。

 NodeList nodes = ...; Set targetElements = new HashSet(); 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); } 

在循环时删除节点将导致不期望的结果,例如错过或重复的结果。 这甚至不是同步和线程安全的问题,但如果节点由循环本身修改。 在这种情况下,大多数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); } } 

根据DOM规范,对node.getElementsByTagName(“…”)的调用结果应该是“实时”的,也就是说,对DOM树的任何修改都将反映在NodeList对象中。 那么,对于符合要求的实现,那就是……

DOM中的NodeList和NamedNodeMap对象是实时的; 也就是说,对底层文档结构的更改将反映在所有相关的NodeList和NamedNodeMap对象中。

( DOM规范 )

因此,当您修改树结构时,符合标准的实现将更改NodeList以反映这些更改。

Practical XML库现在包含NodeListIterator ,它包装NodeList并提供完整的Iterator支持(这似乎是比发布我们在评论中讨论的代码更好的选择)。 如果您不想使用完整的库,请随意复制该类: http : //practicalxml.svn.sourceforge.net/viewvc/practicalxml/trunk/src/main/java/net/sf/practicalxml/ UTIL / NodeListIterator.java?修订= 125&视图=标记

根据DOM Level 3 Core规范,

调用方法node.getElementsByTagName("...")将是对“ 实时NodeList类型的引用。

DOM中的NodeList和NamedNodeMap对象是实时的; 也就是说,对底层文档结构的更改将反映在所有相关的NodeList和NamedNodeMap对象中。 …更改会自动反映在NodeList中,而无需对用户进行进一步操作。

1.1.1 DOM结构模型,第14段。 2


JavaSE 7符合DOM Level 3规范:它实现了实时 NodeList接口并将其定义为类型; 它定义并公开Interface Element上的getElementsByTagName方法,该方法返回实时 NodeList类型。


参考

W3C – 文档对象模型(DOM)级别3核心规范 – getElementsByTagName

JavaSE 7 – 接口元素

JavaSE 7 – NodeList类型

老post,但没有标记为答案。 我的方法是从最后迭代,即

 for (int i = nodes.getLength() - 1; i >= 0; i--) { // do processing, and then e.getParentNode().removeChild(e); } 

有了这个,您不必担心删除时NodeList会变短。