遍历节点列表时删除 DOM 节点

2022-09-02 09:31:31

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


答案 1

在循环时删除节点将导致不良结果,例如丢失或重复的结果。这甚至不是同步和线程安全的问题,而是如果节点被循环本身修改。在这种情况下,Java的大多数迭代器都会抛出一个ConcurrentModificationException,这是NodeList没有考虑到的。

可以通过减小 NodeList 大小和同时递减迭代指针来修复它。只有当我们对每个循环迭代执行一个删除操作时,才能使用此解决方案。

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

因此,假设在遍历 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);
}