【问题标题】:Java 8 Stream API: get all nodes after specific NodeJava 8 Stream API:获取特定节点之后的所有节点
【发布时间】:2026-02-03 19:15:01
【问题描述】:

我有 org.w3c.dom.NodeList 有 5 个节点。 UUID.randomUUID();生成的节点id

Dom 结构:

<section id="3" prefix="s1">
  <paragraph id="value3">
  ...
  </paragraph>
  <paragraph id="value4">
  ...
  </paragraph>
  <paragraph id="value5">
  ...
  </paragraph>
  <paragraph id="value6">
  ...
  </paragraph>
  <paragraph id="value7">
  ...
  </paragraph>
</section>

我需要获取属性 id="value5" 的节点之后的所有节点。所以结果应该包含 id=value6 和 id=value7 的节点。

我创建了Stream&lt;Element&gt;

NodeList children = element.getChildNodes();
Stream<Element> elementStream = IntStream.range(0, children.getLength())
                                         .mapToObj(children::item)
                                         .filter(Element.class::isInstance)
                                         .map(Element.class::cast);

那么,我可以用 Java 8 Stream API 以某种方式做到这一点吗?

【问题讨论】:

  • 尝试通过attribute &gt; 5过滤它们
  • @AndriiAbramov,id 是生成的值。我无法比较它们。
  • 在 jdk-9 中你可以这样做:.dropWhile(i -&gt; i != 5).skip(1)
  • @Eugene,谢谢。不幸的是,我们使用 jdk-8。

标签: java dom java-8 java-stream


【解决方案1】:

你的问题很混乱。但是,如果您只需要获取具有特定属性的元素之后的节点并且它们是有序的,您可以尝试通过元素的属性来过滤元素:

public List<Element> filterElements(NodeList items, int attributeStart){

    List<Element> result = IntStream.range(0, items.getLength())
            .mapToObj(children::item)
            .filter(Element.class::isInstance)
            .map(Element.class::cast)
            .filter(e -> Integer.parseInt(e.getAttribute("id")) > attributeStart)
            .collect(Collectors.toList());

    return result;

}

【讨论】:

  • 非常感谢!但不幸的是随机uuid生成的id值:UUID.randomUUID();我无法比较它们。
  • @AndrewNepogoda 现在我的回答似乎很愚蠢:( UUID 有什么顺序规则吗?我们可以对它们进行排序吗?
  • 抱歉问题不清楚。我们的 id 只是随机值,我们无法对它们进行排序:(
【解决方案2】:

不幸的是,DOM API 不能很好地与其他 API 配合使用。我在this earlier answer 中提供了一个有用的轻量级List 包装器。

有了这个,你可以简单地做:

List<Node> list = XmlUtil.asList(element.getChildNodes());
list = list.subList(
    list.indexOf(element.getOwnerDocument().getElementById("value5"))+1, list.size());

Stream<Element> stream = list.stream()
    .filter(Element.class::isInstance).map(Element.class::cast);

List<Node> list = XmlUtil.asList(element.getChildNodes());
Stream<Element> stream = IntStream.range(
        list.indexOf(element.getOwnerDocument().getElementById("value5"))+1, list.size())
    .mapToObj(list::get).filter(Element.class::isInstance).map(Element.class::cast);

【讨论】:

  • 另一个方向是Stream.iterate(element.getOwnerDocument().getElementById( "value5"), Node::getNextSibling),不幸的是,它无法在null 处结束流,这将使我们回到Java 9,这些问题已得到修复。
  • 感谢您的回答,但我们决定使用 XPath 来完成这项任务。
【解决方案3】:

我们决定使用XPath 来完成这项任务。它看起来好多了:

    private static final String EXPRESSION_TEMPLATE = "//*[@%s='%s']";
    private static final String ID_ATTR = "id";
    // some code here
    @Override
    public NodeList lookupSiblingsAfterId(Element aRootElement, String id) {
        try {
            if (null == id) {
                throw new IllegalArgumentException("Invalid id parameter");
            }
            return (NodeList)xpath.newXPath().evaluate(String.format(EXPRESSION_TEMPLATE, ID_ATTR, id) + "[last()]/following-sibling::*", aRootElement, XPathConstants.NODESET);
        } catch (XPathExpressionException e) {
            LOGGER.error("Can't evaluate XPath expression", e);
            throw new RuntimeException(e);
        }
    }

我希望它对某人有所帮助。

【讨论】:

    最近更新 更多