Javaのfor-eachループでNodeListを処理する

NodeListをfor-eachループで扱えるようにする。

はじめに

Java 1.5(J2SE 5.0)では、コレクションの反復処理の記述を補助するためにfor-eachループが追加されました。for-eachループを用いるとjava.util.Iteratorによる繰り返し処理の記述が簡潔になります。

/* for-eachループを使用しない場合 */
void printListItem(List<String> list) {
    for (Iterator<String> i = list.iterator(); i.hasNext(); ) {
        String item = i.next();
        System.out.println(item);
    }
}
/* for-eachループを使用した場合 */
void printListItem(List<String> list) {
    for (String item : list) {
        System.out.println(item);
    }
}

for-eachループの対象になるのは、java.lang.Iterableインタフェースを実装したクラスです(Iteratorインタフェースを実装したクラスも必要です)。残念なことに、org.w3c.dom.NodeListインタフェースはIterableインタフェースを実装していません。そこで、IterableインタフェースとIteratorインタフェースを実装したクラスを自作して、NodeListをfor-eachループで扱えるようにします。

ソースコード

IterableNodeList

まずはIterableインタフェースを実装したIterableNodeListクラスです。NodeListインタフェースも実装していますが、for-eachループを利用するだけなら実装する必要はありません(IterableNodeListという名前しか思いつかなかったのでNodeListも実装してしまいました)。

/* IterableNodeList.java */
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class IterableNodeList implements Iterable<Node>, NodeList {
    private NodeList nodeList;
    
    public IterableNodeList(NodeList nodeList) {
        this.nodeList = nodeList;
    }
    
    public java.util.Iterator<Node> iterator() {
        return new NodeListIterator(nodeList);
    }
    
    public Node item(int index) {
        return nodeList.item(index);
    }
    
    public int getLength() {
        return nodeList.getLength();
    }
}

NodeListIterator

次にIteratorインタフェースを実装したNodeListIteratorクラスです。

/* NodeListIterator.java */
import java.util.Iterator;
import java.util.NoSuchElementException;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

public class NodeListIterator implements Iterator<Node> {
    private NodeList nodeList;
    private int index = 0;
    private Node cache = null;
    
    public NodeListIterator(NodeList nodeList) {
        this.nodeList = nodeList;
    }
    
    public boolean hasNext() {
        return index < nodeList.getLength();
    }
    
    public Node next() {
        cache = nodeList.item(index++);
        if (cache == null) throw new NoSuchElementException();
        return cache;
    }
    
    public void remove() {
        if (cache == null) throw new IllegalStateException();
        cache.getParentNode().removeChild(cache);
        cache = null;
        index--;
    }
}

removeメソッドにはノードを削除する処理を書きましたが、for-eachループではremoveメソッドを利用しないのでUnsupportedOperationExceptionを投げるだけにすることもできます。

使い方

下の例のようにNodeListオブジェクトをIterableNodeListコンストラクタに渡します。

/* 例: 子ノードを出力する */
void printChildNodes(Node parent) {
    for (Node child : new IterableNodeList(parent.getChildNodes())) {
        System.out.println(child);
    }
}

参考文献