Par exemple, supposons qu'un utilisateur et le montant payé par cet utilisateur soient donnés sous la forme suivante:
public class Payment {
public static void main(String[] args) {
var payments = List.of(
new Payment("A", 10),
new Payment("B", 20),
new Payment("B", 30),
new Payment("C", 40),
new Payment("C", 50),
new Payment("C", 60)
);
}
private String name;
private int value;
public Payment(String name, int value) {
this.name = name;
this.value = value;
}
public String getName() { return name; }
public int getValue() { return value; }
}
Maintenant, pour chaque utilisateur, je voudrais trouver le nombre de paiements, le montant total payé ou le montant maximum. En SQL, il semble que vous puissiez facilement le trouver en combinant GROUP BY
et la fonction de fenêtre, mais comment l'écrire dans Java Stream?
select name, count(*) from payment group by name;
select name, sum(value) from payment group by name;
select name, max(value) from payment group by name;
La politique générale consiste à utiliser Collectors.groupingBy
. Premièrement, le nombre de paiements, c'est-à-dire «group-by-count».
var counts = payments.stream().collect(Collectors.groupingBy(Payment::getName, Collectors.counting()));
counts.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).forEach(System.out::println);
// A=1
// B=2
// C=3
Il existe une méthode bien connue appelée Collectors.counting
, donc il semble bon de l'utiliser. Le montant total payé ensuite. Le point est group-by-sum
, mais cela a également une méthode avec un nom descriptif de Collectors.summingInt
, alors utilisez simplement ceci.
var sums = payments.stream().collect(Collectors.groupingBy(Payment::getName, Collectors.summingInt(Payment::getValue)));
sums.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue()).forEach(System.out::println);
// A=10
// B=50
// C=150
Enfin, "montant maximum payé" = "group-by-max", mais je pense personnellement que c'est le plus controversé. En tant que politique de base, il semble être rapide d'utiliser Collectors.maxBy
.
var maxs = payments.stream().collect(Collectors.groupingBy(Payment::getName, Collectors.maxBy(Comparator.comparingInt(Payment::getValue))));
maxs.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue().get().getValue()).forEach(System.out::println);
// A=10
// B=30
// C=60
À ce stade, le type de la variable «maxs» est «Map <String, Optional est comme un marqueur qui vous avertit qu'il peut être nul, mais ici dans la logique métier, la valeur de
maxsne peut pas être nulle. En bref, «facultatif» ici n'a pas beaucoup de sens, donc je veux m'en débarrasser. En d'autres termes, je veux définir le type de
maxs sur
Map <String, Payment>`, mais dans un tel cas, il semble être rapide de faire comme suit.
var maxs = payments.stream().collect(Collectors.groupingBy(Payment::getName, Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparing(Payment::getValue)), Optional::get)));
maxs.entrySet().stream().map(e -> e.getKey() + "=" + e.getValue().getValue()).forEach(System.out::println);
// A=10
// B=30
// C=60
Cependant, à ce stade, la sensation de magie noire commence à être bonne, alors je veux la modérer (´ ・ ω ・ `)
Recommended Posts