2011-03-22 24 views
57

¿Cuál es la forma más eficiente de iterar a través de todos los elementos DOM en Java?Java: ¿El método más eficiente para iterar sobre todos los elementos en un org.w3c.dom.Document?

Algo como esto, pero para cada elemento de DOM en el actual org.w3c.dom.Document?

for(Node childNode = node.getFirstChild(); childNode!=null;){ 
    Node nextChild = childNode.getNextSibling(); 
    // Do something with childNode, including move or delete... 
    childNode = nextChild; 
} 
+0

Invocación recursiva de Node.getChildNodes? http://download.oracle.com/javase/6/docs/api/org/w3c/dom/Node.html#getChildNodes%28%29 –

Respuesta

102

Básicamente tienes dos formas de iterar sobre todos los elementos:

1. Uso de recursividad (la forma más común creo):

public static void main(String[] args) throws SAXException, IOException, 
     ParserConfigurationException, TransformerException { 

    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory 
     .newInstance(); 
    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); 
    Document document = docBuilder.parse(new File("document.xml")); 
    doSomething(document.getDocumentElement()); 
} 

public static void doSomething(Node node) { 
    // do something with the current node instead of System.out 
    System.out.println(node.getNodeName()); 

    NodeList nodeList = node.getChildNodes(); 
    for (int i = 0; i < nodeList.getLength(); i++) { 
     Node currentNode = nodeList.item(i); 
     if (currentNode.getNodeType() == Node.ELEMENT_NODE) { 
      //calls this method for all the children which is Element 
      doSomething(currentNode); 
     } 
    } 
} 

2. Evitar la repetición usando el método getElementsByTagName() con * como parámetro:

public static void main(String[] args) throws SAXException, IOException, 
     ParserConfigurationException, TransformerException { 

    DocumentBuilderFactory docBuilderFactory = DocumentBuilderFactory 
      .newInstance(); 
    DocumentBuilder docBuilder = docBuilderFactory.newDocumentBuilder(); 
    Document document = docBuilder.parse(new File("document.xml")); 

    NodeList nodeList = document.getElementsByTagName("*"); 
    for (int i = 0; i < nodeList.getLength(); i++) { 
     Node node = nodeList.item(i); 
     if (node.getNodeType() == Node.ELEMENT_NODE) { 
      // do something with the current element 
      System.out.println(node.getNodeName()); 
     } 
    } 
} 

Creo que estas formas son eficientes.
Espero que esto ayude.

+10

Al pasar el índice de iteración como argumento de la función recursiva, puede hacerlo tail-recursive, que está optimizado por el compilador, para evitar el desbordamiento de la pila. – khachik

+95

Creo que es demasiado tarde para evitar el desbordamiento de la pila. Ya estás aquí. – braden

+1

¿Qué le hace pensar que la creación de una lista de nodos para todo el documento es eficiente? Esto significa casi copiar todo el documento. ¿O hay algún tipo de evaluación diferida oculta en 'NodeList' que optimiza las llamadas secuenciales a' item'? – ceving

32

for (int i = 0; i < nodeList.getLength(); i++)

cambio a

for (int i = 0, len = nodeList.getLength(); i < len; i++)

a ser más eficiente. La segunda forma puede ser la mejor ya que tiende a usar un modelo de memoria más plano y predecible.

+1

Necesita al menos 50 puntuaciones de repetición para comentar. Tuve el mismo problema y respondí porque no podía comentar. Tener alguna ayuda alcista;) – nyaray

+2

¿El compilador no optimiza? :-PAG – whomaniac

2

También me encontré con este problema recientemente. Aquí está mi solución. Quería evitar la recursión, así que usé un ciclo while.

Debido a las adiciones y elimina en lugares arbitrarios en la lista, Fui con la implementación LinkedList.

/* traverses tree starting with given node */ 
    private static List<Node> traverse(Node n) 
    { 
    return traverse(Arrays.asList(n)); 
    } 

    /* traverses tree starting with given nodes */ 
    private static List<Node> traverse(List<Node> nodes) 
    { 
    List<Node> open = new LinkedList<Node>(nodes); 
    List<Node> visited = new LinkedList<Node>(); 

    ListIterator<Node> it = open.listIterator(); 
    while (it.hasNext() || it.hasPrevious()) 
    { 
     Node unvisited; 
     if (it.hasNext()) 
     unvisited = it.next(); 
     else 
     unvisited = it.previous(); 

     it.remove(); 

     List<Node> children = getChildren(unvisited); 
     for (Node child : children) 
     it.add(child); 

     visited.add(unvisited); 
    } 

    return visited; 
    } 

    private static List<Node> getChildren(Node n) 
    { 
    List<Node> children = asList(n.getChildNodes()); 
    Iterator<Node> it = children.iterator(); 
    while (it.hasNext()) 
     if (it.next().getNodeType() != Node.ELEMENT_NODE) 
     it.remove(); 
    return children; 
    } 

    private static List<Node> asList(NodeList nodes) 
    { 
    List<Node> list = new ArrayList<Node>(nodes.getLength()); 
    for (int i = 0, l = nodes.getLength(); i < l; i++) 
     list.add(nodes.item(i)); 
    return list; 
    } 
Cuestiones relacionadas