我认为Interface 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();
}
DOM是一个奇怪的野兽,API是由W3C以依赖于语言的方式定义的,然后映射到各种不同的编程语言中,因此Java不能向最初不属于DOM规范的核心DOM接口添加任何特定于Java的内容。
因此,虽然您不能将NodeList
用作流,但您可以轻松地从NodeList
创建流,例如使用
Stream<Node> nodeStream = IntStream.range(0, nodeList.getLength())
.mapToObj(nodeList::item);
然而,有一个很大的警告-DOM NodeList
是live,它反映了自创建列表以来对原始DOM树的更改。如果在DOM树中添加或删除元素,它们可能会神奇地从现有的NodeList中出现或消失,如果在迭代过程中发生这种情况,可能会产生奇怪的效果。如果你想要一个"死"节点列表,你需要将它复制到一个数组或列表中,就像你已经在做的那样。
考虑到引入了默认方法以确保向后兼容性,我不明白为什么这个接口没有stream((函数。
该接口是在Java 8存在之前定义的。由于Stream
在Java 8之前并不存在,因此NodeList
无法支持它
如何将NodeList与Stream API结合使用?
你不能。至少,不是直接的。
如果不鼓励这样做,原因是什么?
它并没有"气馁"。相反,它不受支持。
主要原因是历史。请参见上文。
负责为Java指定org.w3c.dom
API的人员(即W3联盟(可能会推出对Java 8更友好的新版API。然而,这将引入一系列新的兼容性问题。新版本的API将与当前的API不兼容,并且与Java 8之前的JVM也不兼容。
然而,这比让W3 Consortium更新API要复杂得多。
DOM API是在CORBA IDL中定义的,Java API是通过将CORBA Java映射应用于IDL而"生成"的。此映射由OMG指定。。。而不是W3联盟。因此,创建org.w3c.dom
API的"Java8Stream友好型"版本将需要OMG将CORBAJava映射更新为Stream
感知(至少从CORBA兼容性的角度来看,这将是有问题的(,或者中断Javaneneneba API和CORBA之间的连接。
不幸的是,在刷新IDL到Java的映射时,很难发现OMG世界中发生了什么(如果有的话(。。。除非你为OMG成员组织工作,等等。我没有。
下面是一个流用于查找特定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;
}
java8 Stream.iterate
这样使用:
Stream.iterate(0, i -> i + 1)
.limit (nodeList.getLength())
.map (nodeList::item).forEach...
对于java9iterate,有一个iterate的增强版本,而以前的版本也可用:
Stream.iterate(0, i -> i < nodeList.getLength(), i -> i + 1)
.map (nodeList::item).forEach...
迭代的两个版本在Java14 中仍然相同
如果您想将子节点转换为节点流,则变化较小。仅元素:
private Stream<Element> nodeLisToStreamElements(NodeList nodeList) {
return IntStream.range(0, nodeList.getLength())
.mapToObj(nodeList::item)
.filter(node -> node instanceof Element)
.map(node -> (Element) node);
}