read - nodelist java example
EliminaciĆ³n de nodos DOM al atravesar un NodeList (7)
Estoy a punto de eliminar ciertos elementos en un documento XML, utilizando un código como el siguiente:
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);
}
}
¿Interferirá esto con el recorrido adecuado de la Lista de nodos? ¿Alguna otra advertencia con este enfoque? Si esto es totalmente incorrecto, ¿cuál es la forma correcta de hacerlo?
Antiguo post, pero nada marcado como respuesta. Mi enfoque es iterar desde el final, es decir.
for (int i = nodes.getLength() - 1; i >= 0; i--) {
// do processing, and then
e.getParentNode().removeChild(e);
}
Con esto, no tienes que preocuparte de que NodeList se acorte mientras eliminas.
Como ya se mencionó, la eliminación de un elemento reduce el tamaño de la lista pero el contador sigue aumentando (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]
--
--
--
La solución más simple, en mi opinión, sería eliminar la sección i ++ del bucle y hacerlo cuando sea necesario cuando no se eliminó el elemento iterado.
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++;
}
}
El puntero permanece en el mismo lugar cuando se eliminó el elemento iterado. La lista se desplaza sola.
[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]
--
--
--
De acuerdo con la especificación de DOM Level 3 Core,
el resultado de una llamada al método node.getElementsByTagName("...")
será una referencia a un tipo NodeList
" vivo ".
Los objetos NodeList y NamedNodeMap en el DOM están activos; es decir, los cambios en la estructura del documento subyacente se reflejan en todos los objetos relevantes de NodeList y NamedNodeMap. ... los cambios se reflejan automáticamente en el NodeList, sin más acción por parte del usuario.
1.1.1 El modelo de estructura DOM, párr. 2
JavaSE 7 cumple con la especificación DOM Nivel 3: implementa la interfaz NodeList vivo y la define como un tipo; define y expone el método getElementsByTagName
en el elemento de la interfaz , que devuelve el tipo NodeList
vivo .
Referencias
W3C - Modelo de objeto de documento (DOM) Especificación básica de nivel 3 - getElementsByTagName
De acuerdo con la especificación del DOM, el resultado de una llamada a node.getElementsByTagName ("...") se supone que está "en vivo", es decir, cualquier modificación realizada en el árbol DOM se reflejará en el objeto NodeList . Bueno, para las implementaciones conformes, eso es ...
Los objetos NodeList y NamedNodeMap en el DOM están activos; es decir, los cambios en la estructura del documento subyacente se reflejan en todos los objetos relevantes de NodeList y NamedNodeMap.
Entonces, cuando modifique la estructura de árbol, una implementación conforme cambiará el NodeList para reflejar estos cambios.
La biblioteca XML práctica ahora contiene NodeListIterator , que envuelve un NodeList y proporciona soporte completo para el iterador (esto parece una mejor opción que publicar el código que analizamos en los comentarios). Si no desea utilizar la biblioteca completa, siéntase libre de copiar esa clase: http://practicalxml.svn.sourceforge.net/viewvc/practicalxml/trunk/src/main/java/net/sf/practicalxml/util/NodeListIterator.java?revision=125&view=markup
La eliminación de nodos mientras se realiza un bucle causará resultados no deseados, por ejemplo, resultados perdidos o duplicados. Esto ni siquiera es un problema con la sincronización y la seguridad de subprocesos, pero si los nodos son modificados por el propio bucle. La mayoría de los iteradores de Java lanzarán una excepción concurrente de modificación en tal caso, algo que NodeList no tiene en cuenta.
Se puede arreglar disminuyendo el tamaño de NodeList y disminuyendo el puntero del iteraror al mismo tiempo. Esta solución solo se puede utilizar si realizamos una acción de eliminación para cada iteración de bucle.
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);
}
}
Por lo tanto, dado que la eliminación de nodos mientras atraviesa NodeList hará que se actualice NodeList para reflejar la nueva realidad, asumo que mis índices dejarán de ser válidos y esto no funcionará.
Por lo tanto, parece que la solución es hacer un seguimiento de los elementos que se eliminarán durante el recorrido, y luego eliminarlos todos, una vez que ya no se use el 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);
}