Angenommen, ein Benutzer und der von diesem Benutzer bezahlte Betrag werden in der folgenden Form angegeben:
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; }
}
Nun möchte ich für jeden Benutzer die Anzahl der Zahlungen, den gezahlten Gesamtbetrag oder den Höchstbetrag ermitteln. In SQL scheint es, dass Sie es leicht finden können, indem Sie "GROUP BY" und Fensterfunktion kombinieren, aber wie sollten Sie es in Java Stream schreiben?
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;
Die allgemeine Richtlinie lautet "Collectors.groupingBy". Erstens die Anzahl der Zahlungen, dh "Gruppierung nach Anzahl".
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
Es gibt eine bekannte Methode namens "Collectors.counting", daher scheint es gut, diese zu verwenden. Der als nächstes gezahlte Gesamtbetrag. Der Punkt ist "Gruppieren nach Summe", aber dies hat auch eine Methode mit dem beschreibenden Namen "Collectors.summingInt". Verwenden Sie also einfach diese.
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
Schließlich ist "maximal gezahlter Betrag" = "group-by-max", aber ich persönlich halte dies für den umstrittensten. Grundsätzlich scheint es schnell zu gehen, "Collectors.maxBy" zu verwenden.
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
Zu diesem Zeitpunkt ist der Typ der Variablen "maxs" "Map <String, Optional Optional
ist wie ein Marker, der Sie darauf hinweist, dass er möglicherweise null ist, aber hier in der Geschäftslogik kann der Wert von maxs
nicht null sein. Kurz gesagt, "Optional" macht hier nicht viel Sinn, deshalb möchte ich es loswerden. Mit anderen Worten, ich möchte den Typ von "maxs" auf "Map <String, Payment>" setzen, aber in einem solchen Fall scheint es schnell zu gehen, wie folgt vorzugehen.
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
An diesem Punkt beginnt das Gefühl der schwarzen Magie jedoch gut zu sein, daher möchte ich es moderieren (´ ・ ω ・ `).
Recommended Posts