web-dev-qa-db-ja.com

Javaでfor-eachを使用してNodeListを反復処理できますか?

Javaでfor-eachループを使用してNodeListを反復処理したい。 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());
}
34
user2919834

この問題の回避策は簡単であり、ありがたいことに一度だけ実装する必要があります。

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

このユーティリティクラスをプロジェクトに追加し、XmlUtil.asListメソッドのstaticimportをソースコードに追加したら、次のように使用できます。

for(Node n: asList(dom.getElementsByTagName("year"))) {
  …
}
43
Holger
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();
        }
      };
    }
  };
}
8
Ray Hulha

NodeListは単なるインターフェイスであるため、NodeListIterableの両方を実装するクラスを作成して、繰り返し処理することができます。

5
Eel Lee

科学用のハッピーリトルコトリンバージョンの追加:

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

nodeList.forEach { do_something_awesome() }で使用できます

3
Calin

NodeListIterableを実装しないため、拡張されたforループでは使用できません。

2
chrylis

検証済みのソリューションは非常に便利ですが、ここでは有効なソリューションに基づいて改善されたソリューションを共有します。これにより、繰り返しもできますが、使いやすく安全です。

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に感謝します。

1
jalopezsuarez

NodeList(getElementsByTagName()などから作成)の反復中に(JavaScriptを介して)現在のDOM要素が削除されると、要素は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(...)

1
dfielder

org.Apache.commons.collections4.iterators.NodeListIterator およびcom.Sun.xml.internal.ws.util.xml.NodeListIterator

0
Vadzim