Подтвердить что ты не робот

Могу ли я выполнять итерацию через NodeList, используя для каждого в Java?

Я хочу выполнить итерацию через NodeList с использованием цикла for-each в Java. Я работаю с циклом 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());
}
4b9b3361

Ответ 1

Обходной путь для этой проблемы прямолинейный, и, к счастью, вы должны его реализовать только один раз.

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"))) {
  …
}

Ответ 2

Я знаю, что уже поздно на вечеринку, но...
Начиная с Java-8, вы можете написать решение @RayHulha еще более кратко, используя лямбда-выражение (для создания нового 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 -> {
    // ....
});

Ответ 3

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

Ответ 4

Поскольку NodeList - это просто интерфейс, вы можете создать класс, который будет реализовывать как NodeList, так и Iterable, чтобы перебирать его.

Ответ 5

Добавление счастливой маленькой версии kotlin для sience:

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

Затем можно использовать его с nodeList.forEach { do_something_awesome() }

Ответ 6

NodeList не реализует Iterable, поэтому вы не можете использовать его с расширенным циклом for.

Ответ 7

Если текущий элемент DOM удален (через JavaScript) при повторении NodeList (созданного из getElementsByTagName() и, возможно, других), элемент исчезнет из 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(...)

Ответ 8

Проверенное решение очень полезно, но здесь я поделюсь улучшенным решением, основанным на действительном, это также поможет вам выполнить итерации, но при этом будет простым в использовании и безопасным:

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.