Java:最有效的方法来遍历org.w3c.dom.Document中的所有元素?


74

在Java中遍历所有DOM元素的最有效方法是什么?

像这样,但是对于当前的每个DOM元素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;
}


我认为有趣的是,该问题提出了对a的所有元素进行迭代的最有效方法Document,但是没有一个答案对效率进行了任何测试,而效率的唯一提及是“我认为”或类似的推测。
Garret Wilson

Answers:


129

基本上,您可以通过两种方式遍历所有元素:

1.使用递归(我认为是最常见的方式):

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.避免使用getElementsByTagName()带有*as参数的方法进行递归

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

我认为这些方式都很有效。
希望这可以帮助。


11
将迭代索引作为递归函数的参数传递,您可以使其为尾递归,这是由编译器优化的,以避免堆栈溢出。
2011年

128
我认为现在避免堆栈溢出为时已晚。您已经在这里了。
braden 2012年

1
是什么让您认为为整个文档创建节点列表很有效?这意味着几乎要复制整个文档。还是在NodeList优化顺序调用中隐藏了某种延迟评估item
2013年

1
@ceving NodeList是一个接口。实现可以自由执行高级操作。org.apache.xerces.dom.ParentNode中的item(n)实现包括一个高速缓存,但是它用于加快查找速度,而不是节省内存。
Ryan

继续回答#2,但将for循环更改为:for(int i = 0,len = nodeList.getLength(); i <len; i ++)
Andrew

37

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

改成

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

提高效率。

javanna回答的第二种方法可能是最好的,因为它倾向于使用更平坦,可预测的内存模型。


1
您需要至少50代表得分才能发表评论。我遇到了同样的问题并回答了,因为我无法发表评论。有一些upvote-aid;)
nyaray 2013年

上面的避免递归解决方案可以防止程序基于数据使用更多的堆栈存储器。递归的每个步骤都会将更多数据推入堆栈。
安德鲁(Andrew)

2

最近我也偶然发现了这个问题。这是我的解决方案。我想避免递归,所以我使用了while循环。

由于列表中任意位置的添加和删除,我选择了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;
  }
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.