Java流收集计数到字段

Sam*_*ipp 6 java grouping counting java-stream

是否可以使用Collectors.groupingBy()with Collectors.counting()来计数自定义对象的字段,而不是创建映射并随后对其进行转换。

我有一个用户列表,像这样:

public class User {
    private String firstName;
    private String lastName;
    // some more attributes

    // getters and setters
}
Run Code Online (Sandbox Code Playgroud)

我要计算所有具有相同名字和姓氏的用户。因此,我有这样的自定义对象:

public static class NameGroup {
    private String firstName;
    private String lastName;
    private long count;

    // getters and setters
}
Run Code Online (Sandbox Code Playgroud)

我可以使用以下方法收集名称组:

List<NameGroup> names = users.stream()
        .collect(Collectors.groupingBy(p -> Arrays.asList(p.getFirstName(), p.getLastName()), Collectors.counting()))
        .entrySet().stream()
        .map(e -> new NameGroup(e.getKey().get(0), e.getKey().get(1), e.getValue()))
        .collect(Collectors.toList());
Run Code Online (Sandbox Code Playgroud)

使用此解决方案,我必须先将用户分组到地图,然后再将其转换为我的自定义对象。是否可以直接计算所有名称,nameGroup.count以避免在列表(或映射)上重复两次并提高性能?

And*_*eas 1

您可以Arrays.asList(...)通过自己构建映射而不是使用流来最大程度地减少中间对象(例如所有对象)的分配。

这依赖于 your 是可变的这一事实NameGroup

为了使代码更简单,让我们添加两个助手NameGroup

public static class NameGroup {
    // fields here

    public NameGroup(User user) {
        this.firstName = user.getFirstName();
        this.lastName = user.getLastName();
    }

    public void incrementCount() {
        this.count++;
    }

    // other constructors, getters and setters here
}
Run Code Online (Sandbox Code Playgroud)

完成后,您可以实现如下逻辑:

Map<User, NameGroup> map = new TreeMap<>(Comparator.comparing(User::getFirstName)
                                                   .thenComparing(User::getLastName));
users.stream().forEach(user -> map.computeIfAbsent(user, NameGroup::new).incrementCount());
List<NameGroup> names = new ArrayList<>(map.values());
Run Code Online (Sandbox Code Playgroud)

或者,如果您实际上不需要列表,则最后一行可以简化为:

Collection<NameGroup> names = map.values();
Run Code Online (Sandbox Code Playgroud)