所以,现在让我们交易吧。我有Parser类,我可以从html获取链接。我已经使用Jsoup获取链接,然后我想将此链接添加到List。我正在使用StreamAPI来继续。 请查看以下代码片段:
public static List<Link> getLinksFromURL(String URL) {
List<Link> links = new ArrayList<>();
Elements elements = selectElements(URL, "a[href]");
elements.parallelStream().forEach((Parser) -> addLink(links, elements.attr("href")));
return links;
}
有效。但我在处理什么?当我测试这个方法时,我注意到流执行可能会持续一段时间。我可以在我的测试类中得到错误的数据。例如。我检查列表大小。在调试期间一切都很好。但是在运行时我得到的元素很少而不是所有链接,因为尚未执行流。 简单的测试。但有时候size()== 3或更少:
@Test
public void getLinksFromURLTest() {
Assert.assertTrue(Parser.getLinksFromURL("testLinks").size()==4);
}
我的问题是我在流执行时如何等待?我需要获得所有链接:)
小注意:我从我为测试启动的Spark服务器上的本地html中获取了html。
P.S。:如果我的书面不易理解,请告诉我,我会补充解释。
如果你帮助我,我会非常感激。 祝大家好运! :)更新: addLink方法
private static void addLink(List<Link> links, String URL) {
if (!URL.isEmpty() && isLink(URL) && !hasSameLink(links, URL)){
links.add(new Link(URL));
}
}
答案 0 :(得分:3)
您在forEach()
电话中犯了错误。对于每个元素,您在Elements.attr()
变量上调用elements
而不是当前项目的Element.attr()
。你可能想做这样的事情:
forEach(element -> addLink(links, element.attr("href")))
无论如何,您的代码不是线程安全的。如果没有某种形式的同步(这很可能会破坏并行化的好处),您就无法将多个线程写入常规ArrayList
。这也是您在测试中看到不一致结果的可能原因。您应该使用线程安全集合,或者只使用顺序迭代的棒。
或者,您可以将所有逻辑转换为流管道并改为使用收集器:
return elements.parallelStream()
.filter(url -> !url.isEmpty())
.filter(url -> isLink(url))
.distinct()
.map(Link::new)
.collect(Collectors.toList());
答案 1 :(得分:1)
根据doc,您需要同步代码。您可以使用元素数初始化CountdownLatch
,然后在主线程中等待它。