我想在Java中使用for-each循环遍历NodeList
。我让它使用for循环和do-while循环但不适用于每个。
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());
}
答案 0 :(得分:43)
这个问题的解决方法很简单,谢天谢地,你必须只实现一次。
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();
}
}
}
将此实用程序类添加到项目中并为static
方法添加了import
XmlUtil.asList
到源代码后,您可以像这样使用它:
for(Node n: asList(dom.getElementsByTagName("year"))) {
…
}
答案 1 :(得分:10)
我知道派对迟到了,但是......
从Java-8开始,您可以使用lambda表达式(用于创建新的@RayHulha's solution)和默认方法(用于Iterable
)更简洁地编写Iterator.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++);
}
};
}
然后像这样使用它:
NodeList nodeList = ...;
for (Node node : iterable(nodeList)) {
// ....
}
或者相当于这样:
NodeList nodeList = ...;
iterable(nodeList).forEach(node -> {
// ....
});
答案 2 :(得分:8)
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();
}
};
}
};
}
答案 3 :(得分:5)
由于NodeList
只是一个界面,您可以创建一个实现NodeList
和Iterable
的类,以便迭代它。
答案 4 :(得分:2)
NodeList
未实现Iterable
,因此您无法将其与增强型for
循环一起使用。
答案 5 :(得分:2)
为sience添加快乐的小kotlin版本:
fun NodeList.forEach(action: (Node) -> Unit) {
(0 until this.length)
.asSequence()
.map { this.item(it) }
.forEach { action(it) }
}
然后可以将其与nodeList.forEach { do_something_awesome() }
答案 6 :(得分:1)
如果在迭代NodeList(从getElementsByTagName()和其他人创建)时删除当前DOM元素(通过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);
}
}
然后像这样使用它:
new IteratableNodeList(doc.getElementsByTagName(elementType)).
stream().filter(...)
或者:
new IteratableNodeList(doc.getElementsByTagName(elementType)).forEach(...)
答案 7 :(得分:1)
经过验证的解决方案非常有用,但是我在此共享基于有效解决方案的改进解决方案,它可以帮助您进行迭代,但又易于使用且安全:
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();
}
}
}
用法:
for (Node inner : XMLHelper.getChildNodes(node)) { ... }
感谢@Holger。
答案 8 :(得分:1)
一个人可以使用Java8流来迭代NodeList。
NodeList filterList = source.getChildNodes();
IntStream.range(0, filterList.getLength()).boxed().map(filterList::item).forEach(node -> {
});
答案 9 :(得分:0)
在org.apache.commons.collections4.iterators.NodeListIterator
和com.sun.xml.internal.ws.util.xml.NodeListIterator
中可以使用或复制粘贴迭代器实现。
答案 10 :(得分:0)
我想感谢@Calin 对 Kotlin 代码的启发,但我想更进一步,能够在一行中按类型和子类过滤 NodeList 内容
fun <T : Node> NodeList.forEach(clazz : KClass<T>, vararg nodeType: Short, action: (T) -> Unit) {
(0 until this.length).asSequence().map { this.item(it) }
.filter { nodeType.isEmpty() || nodeType.contains(it.nodeType) }
.filter { clazz.isInstance(it) }.map { clazz.java.cast(it) }
.forEach { action(it) }
}
// original variant without any filtering, used for node's attributes
fun NamedNodeMap.forEach(action: (Node) -> Unit) {
(0 until this.length).asSequence().map { this.item(it) }
.forEach { action(it) }
}
用法示例:
xmlDoc.childNodes.forEach(Element::class, Node.ELEMENT_NODE) {
println("tag ${it.tagName} with attributes: ") // 'it' is an Element here
it.attributes.forEach { attr -> println("${attr.nodeName} - ${attr.nodeValue}")}
}