Can I iterate through a NodeList using for-each in Java?
I want to iterate through a NodeList
using a for-each loop in Java. I have it working with a for loop and a do-while loop but not 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());
}
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 static
import
for the XmlUtil.asList
method to your source code you can use it like this:
for(Node n: asList(dom.getElementsByTagName("year"))) {
…
}
I know it is late to the party, but...
Since Java-8 you can write @RayHulha's solution even more concisely by using lambda expression (for creating a new Iterable
) and default method (for 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++);
}
};
}
and then use it like this:
NodeList nodeList = ...;
for (Node node : iterable(nodeList)) {
// ....
}
or equivalently like this:
NodeList nodeList = ...;
iterable(nodeList).forEach(node -> {
// ....
});
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();
}
};
}
};
}
Adding the happy little kotlin version for sience:
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() }