使用 Stream API 的对象的 Sum 属性

2022-01-22 00:00:00 java-8 java java-stream

我目前有以下情况:

我有一个 Report 对象,它可以包含多个 Query 对象.Query 对象具有以下属性: Optional;比较过滤器, 可选的<String>filterChoiceint queryOutput.

I have got a Report object which can contain multiple Query objects. The Query objects have properties: Optional<Filter> comparisonFilter, Optional<String> filterChoice and int queryOutput.

不是每个查询都有比较过滤器,所以我先检查一下.然后,我确保我得到了针对特定过滤器的查询(这不是这里的问题,所以我不会详细讨论这个问题).每个过滤器都有一些选择,其中选择的数量是可变的.

Not every query has a comparison filter, so I first check on that. Then, I make sure I get the queries for a particular filter (which is not the problem here, so I will not discuss this in detail). Every filter has some choices, of which the number of choices is variable.

这里是一个输入示例(这些Query对象都有相同的comparisonFilter):

Here is an example of the input (these Query objects all have the same comparisonFilter):

Query 1 -- Choice: 'First' -- Output: 10
Query 1 -- Choice: 'First' -- Output: 5
Query 1 -- Choice: 'Second' -- Output: 25
Query 1 -- Choice: 'Third' -- Output: 10

现在,我想对每个独特选择的查询输出求和.我目前有这个代码:

Now, I would like to sum the query outputs for every unique choice. I currently have this code:

report
.getQueries()
.stream()
.filter(q -> q.getComparisonFilter().isPresent())
.filter(q -> q.getComparisonFilter().get().equals(view.getFilter().get()))
.forEach(query -> {

    //Sum the query outputs per choice

});

我可以通过创建一个 Map 来做到这一点,其中键是选择,值是查询输入.但后来我需要再次循环遍历 Map 以使用某些东西的值(这在这里并不重要).

I could do this by creating a Map<String, Integer>, where the key is the choice and the value is the query input. But then I would need to loop through the Map again to use the value for something (which is not important here).

输出应该是这样的:

Choice: 'First' -- Summed Output: 15
Choice: 'Second' -- Summed Output: 25
Choice: 'Third' -- Summed Output: 10

但我想直接在流上的 forEach 中使用此汇总输出",但如果这不再可能或不实用,我可以接受.

But I would like to use this 'Summed Output' directly in a forEach on the stream, but if this is not possible or practical anymore, I am okay with that.

我想以Java 8"方式执行此操作,但我似乎不知道如何操作.

I would like to do this the 'Java 8'-way, but I can not seem to find out how.

所以我的问题是:是否可以使用新的 Stream API 缩短此时间?

注意:如果有人对如何使这个问题更笼统(也许是更好的标题和一些概括)有一些想法,请告诉我!

Note: If anyone has some ideas about how I could make this question more general (maybe a better title and some generalizations), please let me know!

推荐答案

如果我理解得很好,你确实在寻找一个 groupingBy 然后你必须通过对它们的 int 属性求和来对值进行分组.

If I understand well, you are indeed looking for a groupingBy and then you have to group the values by summing their int property.

groupingBy 会给你一个 Map<String, List<Query>> 但是下游收集器(Collectors.summingInt 在这种情况)将把列表中 Query 实例的所有 int 值相加,得到一个 Map..p>

The groupingBy will give you a Map<String, List<Query>> but then the downstream collector (Collectors.summingInt in this case) will sum all the int values of the Query instances in the list, resulting in a Map<String, Integer>.

import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.summingInt;
...

Map<String, Integer> map = 
    report.getQueries()
          .stream()
          .filter(q -> q.getComparisonFilter().isPresent())
          .filter(q -> q.getComparisonFilter().get().equals(view.getFilter().get()))
          .collect(groupingBy(q -> q.filterChoice.get(), summingInt(q -> q.queryOutput)));

请注意,您应该检查 filterChoice Optional 是否不为空(可能添加另一个 filter 子句?).您可以查看这个小gist 以获得基本和简化的演示来说明原理.

Note that you should check whether the filterChoice Optional is not empty (maybe add another filter clause?). You can see this small gist for a basic and simplified demo to illustrate the principle.

此外,Optional 类提供了一个合理的 equals 实现,因此过滤器子句可能如下所示:

Also the Optional class provide a sensible implementation of equals so the filter clause could looks like this:

.filter(q -> q.getComparisonFilter().equals(view.getFilter()))

相关文章