web-dev-qa-db-fra.com

Java stream collecte le comptage dans le champ

Est-il possible d'utiliser Collectors.groupingBy() avec Collectors.counting() pour compter dans le champ d'un objet personnalisé au lieu de créer une carte et de la transformer ensuite.

J'ai une liste d'utilisateurs, comme ceci:

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

    // getters and setters
}

Je veux compter tous les utilisateurs avec le même prénom et le même nom. Par conséquent, j'ai un objet personnalisé ressemblant à ceci:

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

    // getters and setters
}

Je peux collecter les groupes de noms en utilisant ceci:

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());

Avec cette solution, je dois d'abord regrouper les utilisateurs sur une carte et la transformer ensuite en mon objet personnalisé. Est-il possible de compter tous les noms directement dans nameGroup.count pour éviter d'itérer deux fois sur la liste (ou la carte) et améliorer les performances?

6
Samuel Philipp

Je ne sais pas quelles sont vos exigences mais j'ai modifié votre classe NameGroup pour accepter une classe User au lieu de prénom et nom. Cela a ensuite éliminé la nécessité de sélectionner les valeurs dans le flux intermédiaire de List et uniquement dans un flux d'Utilisateur. Mais cela nécessite toujours la carte.

      List<NameGroup> names =
            users.stream().collect(Collectors.groupingBy(p -> p,Collectors.counting()))
                          .entrySet().stream()
                          .map(e -> new NameGroup(e.getKey(), e.getValue())).collect(
                              Collectors.toList());
0
WJS
public static class NameGroup {
    // ...
    @Override
    public boolean equals(Object other) {
        final NameGroup o = (NameGroup) other;
        return firstName.equals(o.firstName) && lastName.equals(o.lastName);
    }
    @Override
    public int hashCode() {
        return Objects.hash(firstName, lastName);
    }
    @Override
    public String toString() {
        return firstName + " " + lastName + " " + count;
    }
}

public static void main(String[] args) throws IOException {
    List<User> users = new ArrayList<>();
    users.add(new User("fooz", "bar"));
    users.add(new User("fooz", "bar"));
    users.add(new User("foo", "bar"));
    users.add(new User("foo", "bar"));
    users.add(new User("foo", "barz"));
    users.stream()
        .map(u -> new NameGroup(u.getFirstName(), u.getLastName(), 1L))
        .reduce(new HashMap<NameGroup, NameGroup>(), (HashMap<NameGroup, NameGroup> acc, NameGroup e) -> {
            acc.compute(e, (k, v) -> v == null ? e : new NameGroup(e.firstName, e.lastName, e.count + acc.get(e).count));
            return acc;
        }, (a, b) -> {
            b.keySet().forEach(e -> a.compute(e, (k, v) -> v == null ? e : new NameGroup(e.firstName, e.lastName, e.count + a.get(e).count)));
            return a;
        }).values().forEach(x -> System.out.println(x));
}

Cela produira

fooz bar 2
foo barz 1
foo bar 2
0
w4bo