Java 如何计算流过滤器上的匹配项?

Java 如何计算流过滤器上的匹配项?,java,java-8,java-stream,Java,Java 8,Java Stream,如何计算流过滤器的匹配项?我正在尝试将以下代码重构为java8stream: //java7 int i = 0; for (Node node : response.getNodes()) { Integer id = node.getId(); if (id != null) { node.setContent("This is the id: " + id); i++; } } //java8 response.getNodes()

如何计算流过滤器的匹配项?我正在尝试将以下代码重构为java8
stream

//java7
int i = 0;
for (Node node : response.getNodes()) {
    Integer id = node.getId();
    if (id != null) {
        node.setContent("This is the id: " + id);
        i++;
    }
}

//java8
response.getNodes().stream()
    .filter(node -> node.getId() != null)
    .forEach(node -> node.setValue("This is the id: " + node.getId()));
现在如何获得已应用的筛选元素的计数?
旁白:在旧代码中,我可以多次重用
整数id
。如何使用streams实现同样的效果?

由于
setValue
是一个副作用函数,您可以使用
peek

long i = response.getNodes()
                 .stream()
                 .filter(node -> node.getId() != null)
                 .peek(node -> node.setValue("This is the id: " + node.getId()))
                 .count();
我不喜欢这种方法,因为peak是用来调试的(这样就可以了)。请注意,在Java 9中,
count()
如果可以直接从源代码计算计数,则可能无法执行流管道(我认为这里不是这种情况,因为您应用了过滤,但最好记住这一点)

旁白:在旧代码中,我可以多次重用整数id 时代。如何使用流实现同样的效果

这取决于您的用例,因为API没有元组,所以您最好的机会是创建一个类,比如说
Tuple2
,这样您就可以将每个节点映射到一个新的元组并重用id

比如:

.stream().map(node -> new Tuple2<>(node, node.getId()).moreStreamOps(...);
                                                      ^
                                                      |
                 at that point you have a Stream<Tuple2<Node, Integer>> 
                 from which you can grab the id with Tuple2#getSecond
.stream().map(node->new Tuple2(node,node.getId()).moreStreamOps(…);
^
|
在这一点上,你有一条小溪
从中可以使用Tuple2#getSecond获取id

在您的例子中,如果您停留在一个节点流中,您可以使用
getId()获取id
随时。

为什么不添加外部变量作为计数器,只需在
forEach
中添加增量操作?@nikis,这将无法处理局部变量,因为从lambda中使用的环境中捕获的变量必须是有效的最终变量。因此,这将比您想象的更麻烦。@Jesper@nikis您可以使用一个
LongAdder
实例并不断增加它。这个类就是为了这个目的而添加到Java 8中的。@Jesper你的评论让我更深入地了解了Java 8的功能,我花了太多时间在
C#LINQ
天堂=)之外的
Java
王国感谢你的洞察力。然后,我将坚持使用“旧”方法,因为这些副作用可能不适用于流api。@如果您知道自己在做什么,那么听起来不错,这不是问题。您不能总是避免副作用,因为Java无论如何都不是一种功能性语言(即使有一堆新功能)。您可能会将任务分成两个不同的流操作:第一个使用forEach()设置id,第二个使用count()计算节点数.@isnot2bad是的,如果您需要两个终端操作,我通常会这样做/建议这样做(性能不是一个问题,也不是您目前需要优化的问题),但在这种情况下,
forEach
很可能与
peek
相同,因此可以一次完成。@alexic。你说得对。但forEach在某种程度上对我来说更为正确——尽管实际上并没有区别。也许他们应该将
peek
重命名为
foreach,然后
只是为了消除这种不好的感觉……;)