是否可以将Collectors.groupingBy()Collectors.counting()一起使用以计数到自定义对象的字段,而不是随后创建地图并对其进行转换。

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

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

    // getters and setters
}

我要计算所有具有相同名字和姓氏的用户。因此,我有如下所示的自定义对象:
public static class NameGroup {
    private String firstName;
    private String lastName;
    private long count;

    // getters and setters
}

我可以使用以下方法收集名称组:
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());

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

最佳答案

您可以最小化中间对象的分配,例如所有Arrays.asList(...)对象,都可以自己构建地图,而不要使用流式传输。

这取决于您的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
}

有了它,您可以实现如下逻辑:
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());

或者,如果您实际上不需要列表,则可以将最后一行简化为:
Collection<NameGroup> names = map.values();

07-23 10:16