Java 8 Stream API - Auswahl nur Werte nach Sammler.groupingBy(..)
Sagen, ich habe die folgende Sammlung von Student
Objekte, die aus dem Namen(String), Alter(int) und Stadt(String).
Ich versuche, mit Java das Stream-API, um die folgenden sql-like behavior:
SELECT MAX(age)
FROM Students
GROUP BY city
Nun, ich fand zwei verschiedene Möglichkeiten, dies zu tun:
final List<Integer> variation1 =
students.stream()
.collect(Collectors.groupingBy(Student::getCity, Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge())))
.values()
.stream()
.filter(Optional::isPresent)
.map(Optional::get)
.map(Student::getAge)
.collect(Collectors.toList());
Ist und das andere:
final Collection<Integer> variation2 =
students.stream()
.collect(Collectors.groupingBy(Student::getCity,
Collectors.collectingAndThen(Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()),
optional -> optional.get().getAge())))
.values();
In beide Richtungen, man muss .values() ...
und filter die leeren Gruppen zurückgegeben, die von der collector.
Gibt es eine andere Weise, dieses zu erzielen erforderlich Verhalten?
Diese Methoden erinnern mich an over partition by
sql-Anweisungen...
Dank
Edit: Alle Antworten waren wirklich interessant, aber leider ist dies nicht das, was ich gesucht hatte, da das, was ich versuche zu bekommen ist, nur die Werte. Ich brauche nicht den Schlüssel, nur die Werte.
Ich meine nicht rufen
values()
da brauche ich nicht die Tasten an alleDie Umsetzung muss verwenden Sie die Tasten, um herauszufinden, wie Sie die Gruppe der Elemente. Sie können entsorgen Sie die Tasten danach, aber das bedeutet nicht, Sie brauchen Sie nicht in der Zwischenzeit.
InformationsquelleAutor Ghost93 | 2016-02-29
Du musst angemeldet sein, um einen Kommentar abzugeben.
Nicht immer stick mit
groupingBy
. ManchmaltoMap
ist das, was Sie brauchen:Hier erstellen Sie einfach eine
Map
wo die Schlüssel sind Städte und Werte sind Alter. In dem Fall, wenn mehrere Studenten die gleiche Stadt, merge-Funktion verwendet, die nur wählt maximale Alter hier. Es ist schneller und sauberer.Ich habe eine
groupingBy
- basierte Lösung, ist fast so einfach, aber solange der OP interessiert sich in dem Alter nur, dastoMap
Lösung klar, ist die einfachste.InformationsquelleAutor Tagir Valeev
Als Ergänzung zu Tagir tolle Antwort mit
toMap
stattgroupingBy
ist, hier die kurze Lösung, wenn Sie wollen, zu bleiben, um zugroupingBy
:Beachten Sie, dass diese drei arg
reducing
Sammler Uhrt bereits eine mapping-operation, so brauchen wir nicht verschachteln mit einemmapping
Sammler, noch weiter, indem er eine Identität Wert vermeidet den Umgang mitOptional
. Seit Alters her stets positiv, die Bereitstellung-1
ausreichend ist und da eine Gruppe immer mindestens ein element der Identität Wert wird nie als Ergebnis.Dennoch denke ich, Tagir ist
toMap
basierte Lösung ist vorzuziehen, in diesem Szenario.Den
groupingBy
basierte Lösung wird interessanter, wenn Sie wollen, Holen Sie sich die aktuellen Studenten für das maximale Alter, e.ggut, tatsächlich, auch dies kann auch ausgedrückt werden mit der
toMap
Sammler:Können Sie express fast alles mit beiden Sammler, aber
groupingBy
hat den Vorteil auf seiner Seite-wenn Sie möchten, führen Sie eine veränderlich Reduzierung auf die Werte.toMap
, so Bedarf es komplexer Szenarien zu bekommen, die einen Vorteil ausgroupingBy
hier...Dennoch ist der Ansatz interessant ist. Ich habe nie kombiniert
groupingBy
mit zwei-arg/drei-argreduce
. Wahrscheinlich ist es manchmal nützlich...Valeev: das ist, warum ich fügte hinzu, es trotz Ihrer Antwort, besser für diesen Anwendungsfall.
Nicht
Collectors.collectingAndThen(Collectors.maxBy(Comparator.comparingInt(Student::getAge)), Optional::get)
mehr straight-forward-obwohl? (Für das zweite Beispiel.)Sie können
BinaryOperator.maxBy(Comparator.comparingInt(Student::getAge))
als merge-Funktion.InformationsquelleAutor Holger
Der zweite Ansatz fordert
get()
auf eineOptional
; dies ist in der Regel eine schlechte Idee, weil Sie nicht wissen, ob das optional leer sein wird oder nicht (verwenden SieorElse()
,orElseGet()
,orElseThrow()
Methoden statt). Während Sie könnte argumentieren, dass in diesem Fall gibt es immer einen Wert, da Sie erzeugen die Werte, die von der studentischen Liste selbst, das ist etwas im Auge zu behalten.Sie auf dieser Grundlage könnte dann die Variante 2 in:
Obwohl es beginnt wirklich zu schwierig zu Lesen sein, werde ich wohl die Variante 1:
Map.values()
außerhalb dercollect
(obwohl ich alles andere drin ist).Ja, das würde verbessern ein wenig die Lesbarkeit. Als Referenz, ich lasse es hier:
final Collection<Integer> variation2 = students.stream().collect(groupingBy(Student::getCity, collectingAndThen(mapping(Student::getAge, maxBy(naturalOrder())), Optional::get))).values();
InformationsquelleAutor Alexis C.
InformationsquelleAutor Raghu K Nair
InformationsquelleAutor Surabhi