Удаление узлов DOM при прохождении NodeList

Я собираюсь удалить определенные элементы в документе 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

Итак, учитывая, что удаление узлов при обходе 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);
}

Ответ 2

Удаление узлов при циклировании приведет к нежелательным результатам, например. либо пропущенных или дублированных результатов. Это даже не проблема синхронизации и безопасности потоков, но если узлы модифицируются самим циклом. В большинстве случаев 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);
  }
}

Ответ 3

В соответствии с спецификацией DOM результат вызова node.getElementsByTagName( "..." ) должен быть "живым", то есть любая модификация, внесенная в дерево DOM, будет отражена в Объект NodeList. Ну, для соответствия реализаций, то есть...

Объекты NodeList и NamedNodeMap в DOM живут; т.е. изменения в базовая структура документа отражается во всех соответствующих NodeList и Объекты NamedNodeMap.

(Спецификация DOM)

Итак, когда вы изменяете древовидную структуру, соответствующая реализация изменит NodeList, чтобы отразить эти изменения.

Ответ 4

Библиотека Практическая 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

Ответ 5

Согласно спецификации DOM Level 3 Core,

результат вызова метода node.getElementsByTagName("...") будет ссылкой на тип live "NodeList.

Объекты NodeList и NamedNodeMap в DOM являются живыми; то есть изменения в структуре базового документа отражаются во всех соответствующих объектах NodeList и NamedNodeMap.... изменения автоматически отражаются в NodeList, без дополнительных действий с пользовательской частью.

1.1.1 Модель структуры DOM, пара. 2

JavaSE 7 соответствует спецификации DOM Level 3: она реализует интерфейс live NodeList и определяет его как тип; он определяет и предоставляет метод getElementsByTagName на Элементе интерфейса, который возвращает тип live NodeList.


Ссылки

W3C - Объектная модель документа (DOM) Уровень 3 Основная спецификация - getElementsByTagName

JavaSE 7 - Элемент интерфейса

JavaSE 7 - Тип NodeList

Ответ 6

Старый пост, но ничего не помечено как ответ. Мой подход заключается в повторении с конца, т.е.

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

При этом вам не нужно беспокоиться о том, что NodeList становится короче во время удаления.