Java 8的收集器API

7
这个问题涉及使用Java 8收集器映射和分组元素。我有一个Patient列表,每个Patient都有他访问过的部门列表。假设我想要使用Java 8中的新收集器API来计算每个部门被访问的次数,那么如何按照部门列表进行分组呢?
谢谢!
注意:我知道我可以在每个部门中放置一个计数器,但这个问题仅用于学习新的Java 8功能。
代码:
public class Patient {
    private String id;
    private List<Department> history;
    /* some more getters/setters come here */
}

在我的主方法中,我有一个Patient列表。我想知道每个Department被访问了多少次,而不需要编辑Department类(或扩展或装饰它)。
我认为我必须取一个Patient流并将它们映射到一个dept->count的映射中(每个患者访问哪个dept的次数)。拥有这些映射流 - 我不知道如何将这些映射分组并计算它们的数量。

这个可能最好通过编码来解释。请定义您的患者和部门代码,以便每个人都可以进行连接。 - Maarten Bodewes
@owlstead:我添加了类Patient的字段。这应该很简单,但我找不到如何做。请注意,我没有添加Department的代码,因为它没有意义,可以轻松地作为一个字符串。 - Eugene Krapivin
1个回答

11

首先,我假设如果一个患者去了某个科室超过一次,那么该科室将在患者的历史列表中出现多次。

如果是这种情况,那么我们可以简单地将患者流flatMap为科室流,其中科室的出现表示患者的一次就诊。

然后我们可以将其传递给groupingBy收集器。我们要按科室本身进行分类,因此分类器函数是标识函数。

对于值,如果不存在科室条目,我们希望将1作为值放入;如果存在,则将1添加到现有值中。已经有一个名为counting()的收集器可以为我们完成此操作,因此我们只需将其作为下游收集器传递给groupingBy()即可。

最终代码如下:

import static java.util.stream.Collectors.*;

List<Patient> patients = ... ;

Map<Department, Long> frequency =
    patients.stream()
        .flatMap(p -> p.getHistory().stream())
        .collect(groupingBy(d -> d, counting()));

请注意,如果某个部门尚未被访问,则它将在地图上不存在。如果您有一个部门列表,可以这样填充零值:

departments.forEach(d -> frequency.putIfAbsent(d, 0L));

或者如果你想在get一侧填充零,

long numVisits = frequency.getOrDefault(dept, 0L);

看起来很简单,而且似乎有效。不过你需要注意的是,你已经静态导入了 Collectors。能否请您解释一下这个解决方案的逻辑? - Eugene Krapivin
1
@nocgod 我想我在你要求解释的同时正在输入解释。 :-) - Stuart Marks
1
我猜你是没错了 :) 我得做个小改变才能进入这种函数式lambda风格 - 自从我的PPL课程和scheme语言以来就没有碰过它。再次感谢! - Eugene Krapivin

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接