83 votes

Java : La méthode la plus efficace pour itérer sur tous les éléments d'un document org.w3c.dom.Document ?

Quelle est la manière la plus efficace d'itérer à travers tous les éléments du DOM en Java ?

Quelque chose comme ça, mais pour chaque élément du DOM sur le site actuel. 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 votes

Invocation récursive de Node.getChildNodes ? download.oracle.com/javase/6/docs/api/org/w3c/dom/

1 votes

Je pense qu'il est intéressant que la question posée aux le plus efficace pour itérer sur tous les éléments d'un fichier Document Mais aucune des réponses ne comportait de tests d'efficacité, et la seule mention de l'efficacité était "je pense" ou des suppositions similaires.

138voto

javanna Points 24751

En fait, il y a deux façons d'itérer sur tous les éléments :

1. Utilisation de la récursion (la manière la plus courante, je pense) :

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. Éviter la récursion en utilisant getElementsByTagName() méthode avec * comme paramètre :

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());
        }
    }
}

Je pense que ces méthodes sont toutes deux efficaces.
J'espère que cela vous aidera.

11 votes

En passant l'index d'itération comme argument à la fonction récursive, vous pouvez la rendre récursive à la queue, ce qui est optimisé par le compilateur, pour éviter le débordement de la pile.

141 votes

Je pense qu'il est trop tard pour éviter le débordement de pile. Vous êtes déjà là.

1 votes

Qu'est-ce qui vous fait penser que la création d'une liste de nœuds pour l'ensemble du document est efficace ? Cela signifie qu'il faut pratiquement copier l'ensemble du document. Ou bien y a-t-il une sorte d'évaluation retardée cachée dans NodeList l'optimisation des appels séquentiels à item ?

37voto

Andrew Points 144

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

changer pour

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

pour être plus efficace.

La deuxième façon de répondre à javanna est peut-être la meilleure car elle tend à utiliser un modèle de mémoire plus plat et prévisible.

1 votes

Vous devez avoir au moins 50 points de rep pour commenter. J'ai eu le même problème et j'ai répondu parce que je ne pouvais pas commenter. Prenez un peu d'upvote-aid ;)

0 votes

La solution ci-dessus consistant à éviter la récursion empêche le programme d'utiliser davantage de mémoire de pile en fonction des données. Chaque étape de la récursion pousse plus de données dans la pile.

2voto

mike Points 1239

Je suis également tombé sur ce problème récemment. Voici ma solution. Je voulais éviter la récursion, j'ai donc utilisé une boucle while.

A cause des ajouts et retraits dans des endroits arbitraires de la liste, j'ai opté pour le LinkedList mise en œuvre.

/* 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;
  }

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X