如何将 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 一起使用?的主要内容,如果未能解决你的问题,请参考以下文章