如何将 org.w3c.dom.NodeList 与 Java 8 Stream API 一起使用?

Posted

技术标签:

【中文标题】如何将 org.w3c.dom.NodeList 与 Java 8 Stream API 一起使用?【英文标题】:How to use org.w3c.dom.NodeList with Java 8 Stream API? 【发布时间】:2014-06-15 03:10:26 【问题描述】:

我相信接口org.w3c.dom.NodeList 缺少stream() 函数以利用Java 8 的Stream API 的优势。考虑到引入默认方法以确保向后兼容,我不明白为什么这个接口没有stream() 功能。

所以我的问题是:

如何将NodeList 与 Stream API 结合使用? 如果不鼓励这样做,原因是什么?

提前致谢!

编辑:我目前正在使用这个实用程序包装器:

private static Stream<Node> nodeStream(NodeList list) 
    List<Node> nodes = new ArrayList<>();

    for (int n = 0; n < list.getLength(); ++n) 
        nodes.add(list.item(n));
    

    return nodes.stream();

【问题讨论】:

【参考方案1】:

DOM 是一头奇怪的野兽,API 由 W3C 以独立于语言的方式定义,然后映射到各种不同的编程语言中,因此 Java 无法在核心 DOM 接口中添加任何特定于 Java 的内容。首先是 DOM 规范的一部分。

因此,虽然您不能将NodeList as 用作流,但您可以轻松地 NodeList 创建流,例如使用

Stream<Node> nodeStream = IntStream.range(0, nodeList.getLength())
                                   .mapToObj(nodeList::item);

但是,有一个重要的警告 - DOM NodeList活动的,它反映了自创建列表以来对原始 DOM 树的更改。如果您在 DOM 树中添加或删除元素,它们可能会神奇地从现有的 NodeList 中出现或消失,如果这发生在迭代中间,这可能会导致奇怪的效果。如果你想要一个“死”节点列表,你需要将它复制到一个数组或列表中,就像你已经在做的那样。

【讨论】:

更新节点时是否触发了事件?有什么方法可以在文档更改时实时更新 GUI 中的文本?【参考方案2】:

考虑到引入默认方法来保证向后兼容,我不明白为什么这个接口没有stream()函数。

接口是在 Java 8 存在之前定义的。由于Stream 在 Java 8 之前不存在,因此NodeList 无法支持它。

如何将 NodeList 与 Stream API 结合使用?

你不能。至少,不是直接的。

如果不鼓励这样做,原因是什么?

这不是“气馁”。而是不支持。

主要原因是历史。见上文。

负责为 Java 指定 org.w3c.dom API 的人(即 W3 联盟)可能会推出对 Java 8 更友好的 API 的新版本。但是,这会引入一堆新的兼容性问题。新版本的 API 不会与当前版本二进制兼容,也不会与 Java 8 之前的 JVM 兼容。


但是,这比让 W3 联盟更新 API 更复杂。

DOM API 是在 CORBA IDL 中定义的,而 Java API 是通过将 CORBA Java 映射应用到 IDL 来“生成”的。此映射由 OMG 指定……而不是 W3 联盟。因此,创建org.w3c.dom API 的“Java 8 流友好”版本将需要让 OMG 更新 CORBA Java 映射以支持Stream(至少从 CORBA 兼容性的角度来看这是有问题的)或破坏Java API 和 CORBA 之间的连接。

不幸的是,在刷新 IDL 到 Java 映射的过程中,很难了解 OMG 世界中发生了什么(如果有的话)……除非您为 OMG 成员组织等工作。我没有。

【讨论】:

除非我误解了您的意思,否则您“由于 Stream 在 Java 8 之前不存在,因此 NodeList 无法支持它”的说法是没有意义的。该问题明确询问为什么没有将默认方法添加到 NodeList - 即类似于如何将默认流方法添加到 Collection。 就像我说的,稍后在我的回答中,Oracle 不控制 API,让 OMG 和/或 W3C 修复它并不简单。所以这不仅仅是甲骨文“修复”它的问题。【参考方案3】:

这是一个使用流来查找特定 NodeList 元素的示例:

private String elementOfInterest;       // id of element
private String elementOfInterestValue;  // value of element

public boolean searchForElementOfInterest(Document doc)

        boolean bFound=false;
        NodeList nList = doc.getElementsByTagName("entity");

        // since NodeList does not have stream implemented, then use this hack
        Stream<Node> nodeStream = IntStream.range(0, nList.getLength()).mapToObj(nList::item);
        // search for element of interest in the NodeList
        if(nodeStream.parallel().filter(this::isElementOfInterest).collect(Collectors.toList()).size() > 0)
                bFound=true;

        return bFound;


private boolean isElementOfInterest(Node nNode)

        boolean bFound=false;
        assert(nNode != null);
        if (nNode.getNodeType() == Node.ELEMENT_NODE) 
                Element eElement = (Element) nNode;
                String id = eElement.getElementsByTagName("id").item(0).getTextContent();
                String data = eElement.getElementsByTagName("data").item(0).getTextContent();
                if (id.contentEquals(elementOfInterest) && data.contentEquals(elementOfInterestValue))
                        bFound = true;
        
        return bFound;

【讨论】:

请注意,要确定流是否具有匹配条件的元素,在Stream 类上使用anyMatch(Predicate) 方法会更有效(也更容易阅读)。例如,在上面,您可以简单地说:return nodeStream().parallel().anyMatch(this::isElementOfInterest);【参考方案4】:

java8 Stream.iterate 像这样使用:

    Stream.iterate(0, i -> i + 1)
          .limit (nodeList.getLength())
          .map (nodeList::item).forEach...

对于java9 iterate,有一个迭代的增强版本,而以前的版本也可用:

    Stream.iterate(0, i -> i < nodeList.getLength(), i -> i + 1)
          .map (nodeList::item).forEach...

两个版本的迭代在 Java14 中仍然相同

【讨论】:

以上是关于如何将 org.w3c.dom.NodeList 与 Java 8 Stream API 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章

如何将thinkcmf导入eclipse

如何将CString转换成wstring

如何将Ios文件上传到

Qt如何将文字变成图片?

如何将Bitmap保存为本地图片文件?

在MATLAB中如何将图导出