遍历NodeList时删除DOM节点

ski*_*ppy 17 java xml 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);
  }
}
Run Code Online (Sandbox Code Playgroud)

这会干扰NodeList的正确遍历吗?用这种方法还有其他注意事项吗?如果这是完全错误的,那么正确的做法是什么?

ski*_*ppy 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);
}
Run Code Online (Sandbox Code Playgroud)

  • 好的,我现在看到你在说什么.向后倒数. (2认同)

Alg*_*gok 10

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


Dir*_*irk 7

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

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

(DOM规范)

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

  • 这意味着我的索引在遍历期间变得无效,对吧? (2认同)