我可以在 Java 中使用 for-each 遍历 NodeList 吗?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/19589231/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-12 18:35:01  来源:igfitidea点击:

Can I iterate through a NodeList using for-each in Java?

javaxmldom

提问by user2919834

I want to iterate through a NodeListusing a for-each loop in Java. I have it working with a for loop and a do-while loop but not for-each.

我想NodeList在 Java 中使用 for-each 循环进行迭代。我让它与 for 循环和 do-while 循环一起工作,但不是 for-each。

NodeList nList = dom.getElementsByTagName("year");
do {
    Element ele = (Element) nList.item(i);
    list.add(ele.getElementsByTagName("MonthId").item(0).getTextContent());
    i++;
} while (i < nList.getLength());

NodeList nList = dom.getElementsByTagName("year");

for (int i = 0; i < nList.getLength(); i++) {
    Element ele = (Element) nList.item(i);
    list.add(ele.getElementsByTagName("MonthId").item(0).getTextContent());
}

采纳答案by Holger

The workaround for this problem is straight-forward, and, thankfully you have to implements it only once.

此问题的解决方法很简单,幸运的是,您只需实现一次即可。

import java.util.*;
import org.w3c.dom.*;

public final class XmlUtil {
  private XmlUtil(){}

  public static List<Node> asList(NodeList n) {
    return n.getLength()==0?
      Collections.<Node>emptyList(): new NodeListWrapper(n);
  }
  static final class NodeListWrapper extends AbstractList<Node>
  implements RandomAccess {
    private final NodeList list;
    NodeListWrapper(NodeList l) {
      list=l;
    }
    public Node get(int index) {
      return list.item(index);
    }
    public int size() {
      return list.getLength();
    }
  }
}

Once you have added this utility class to your project and added a staticimportfor the XmlUtil.asListmethod to your source code you can use it like this:

一旦您将此实用程序类添加到您的项目并将staticimportXmlUtil.asList方法添加到您的源代码中,您就可以像这样使用它:

for(Node n: asList(dom.getElementsByTagName("year"))) {
  …
}

回答by chrylis -cautiouslyoptimistic-

NodeListdoes not implement Iterable, so you cannot use it with the enhanced forloop.

NodeList未实现Iterable,因此您不能将其与增强for循环一起使用。

回答by Eel Lee

As NodeListis just an interface, you could create a class which would implement both NodeListand Iterable, in order to iterate through it.

由于NodeList只是一个接口,您可以创建一个同时实现NodeList和的类Iterable,以便对其进行迭代。

回答by Ray Hulha

public static Iterable<Node> iterable(final NodeList n) {
  return new Iterable<Node>() {

    @Override
    public Iterator<Node> iterator() {

      return new Iterator<Node>() {

        int index = 0;

        @Override
        public boolean hasNext() {
          return index < n.getLength();
        }

        @Override
        public Node next() {
          if (hasNext()) {
            return n.item(index++);
          } else {
            throw new NoSuchElementException();
          }  
        }

        @Override
        public void remove() {
          throw new UnsupportedOperationException();
        }
      };
    }
  };
}

回答by dfielder

If the current DOM element is removed (via JavaScript) while iterating a NodeList (created from getElementsByTagName() and maybe others), the element will disappear from the NodeList. This makes correct iteration of the NodeList more tricky.

如果当前 DOM 元素在迭代 NodeList(从 getElementsByTagName() 或其他创建)时被删除(通过 JavaScript),则该元素将从 NodeList 中消失。这使得 NodeList 的正确迭代变得更加棘手。

public class IteratableNodeList implements Iterable<Node> {
    final NodeList nodeList;
    public IteratableNodeList(final NodeList _nodeList) {
        nodeList = _nodeList;
    }
    @Override
    public Iterator<Node> iterator() {
        return new Iterator<Node>() {
            private int index = -1;
            private Node lastNode = null;
            private boolean isCurrentReplaced() {
                return lastNode != null && index < nodeList.getLength() &&
                       lastNode != nodeList.item(index);
            }

            @Override
            public boolean hasNext() {
                return index + 1 < nodeList.getLength() || isCurrentReplaced();
            }

            @Override
            public Node next() {
                if (hasNext()) {
                    if (isCurrentReplaced()) {
                        //  It got removed by a change in the DOM.
                        lastNode = nodeList.item(index);
                    } else {
                        lastNode = nodeList.item(++index);
                    }
                    return lastNode;
                } else {
                    throw new NoSuchElementException();
                }
            }

            @Override
            public void remove() {
                throw new UnsupportedOperationException();
            }
        };
    }

    public Stream<Node> stream() {
        Spliterator<Node> spliterator =
            Spliterators.spliterator(iterator(), nodeList.getLength(), 0);
        return StreamSupport.stream(spliterator, false);
    }
}

Then use it like this: new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...)

然后像这样使用它: new IteratableNodeList(doc.getElementsByTagName(elementType)). stream().filter(...)

Or: new IteratableNodeList(doc.getElementsByTagName(elementType)).forEach(...)

或者: new IteratableNodeList(doc.getElementsByTagName(elementType)).forEach(...)

回答by Calin

Adding the happy little kotlin version for sience:

为科学添加快乐的小 kotlin 版本:

fun NodeList.forEach(action: (Node) -> Unit) {
    (0 until this.length)
            .asSequence()
            .map { this.item(it) }
            .forEach { action(it) }
}

One can then use it with nodeList.forEach { do_something_awesome() }

然后可以使用它 nodeList.forEach { do_something_awesome() }

回答by Thomas Fritsch

I know it is late to the party, but...
Since Java-8 you can write @RayHulha's solutioneven more concisely by using lambda expression (for creating a new Iterable) and default method (for Iterator.remove):

我知道聚会已经晚了,但是...
从 Java-8 开始,您可以通过使用 lambda 表达式(用于创建 new )和默认方法(用于)更简洁地编写@RayHulha 的解决方案IterableIterator.remove

public static Iterable<Node> iterable(final NodeList nodeList) {
    return () -> new Iterator<Node>() {

        private int index = 0;

        @Override
        public boolean hasNext() {
            return index < nodeList.getLength();
        }

        @Override
        public Node next() {
            if (!hasNext())
                throw new NoSuchElementException();
            return nodeList.item(index++); 
        }
    };
}

and then use it like this:

然后像这样使用它:

NodeList nodeList = ...;
for (Node node : iterable(nodeList)) {
    // ....
}

or equivalently like this:

或等效地像这样:

NodeList nodeList = ...;
iterable(nodeList).forEach(node -> {
    // ....
});

回答by jalopezsuarez

The validated solution is very useful, but here I share an improved solution based on the valid one, this helps you iterate as well, but easy to use, and secure:

经过验证的解决方案非常有用,但在这里我分享一个基于有效解决方案的改进解决方案,这也有助于您进行迭代,但易于使用且安全:

public class XMLHelper {
    private XMLHelper() { }

    public static List<Node> getChildNodes(NodeList l) {
        List<Node> children = Collections.<Node>emptyList();
        if (l != null && l.getLength() > 0) {
            if (l.item(0) != null && l.item(0).hasChildNodes()) {
                children = new NodeListWrapper(l.item(0).getChildNodes());
            }
        }
        return children;
    }

    public static List<Node> getChildNodes(Node n) {
        List<Node> children = Collections.<Node>emptyList();
        if (n != null && n.hasChildNodes()) {
            NodeList l = n.getChildNodes();
            if (l != null && l.getLength() > 0) {
                children = new NodeListWrapper(l);
            }
        }
        return children;
    }

    private static final class NodeListWrapper extends AbstractList<Node> implements RandomAccess {
        private final NodeList list;
        NodeListWrapper(NodeList l) {
            list = l;
        }
        public Node get(int index) {
            return list.item(index);
        }
        public int size() {
            return list.getLength();
        }
    }

}

}

Usage:

用法:

 for (Node inner : XMLHelper.getChildNodes(node)) { ... }

Thanks @Holger.

谢谢@霍尔格。

回答by Vadzim

There are ready to use or copypaste iterator implementations in org.apache.commons.collections4.iterators.NodeListIteratorand com.sun.xml.internal.ws.util.xml.NodeListIterator.

有准备使用或copypaste迭代器实现中org.apache.commons.collections4.iterators.NodeListIteratorcom.sun.xml.internal.ws.util.xml.NodeListIterator