Das JEP 461: Stream Gatherers Java 22-Vorschau Sprachfunktion fügt Unterstützung für Sammleroperationen hinzu, die verwendet werden können, um den Stream nach eindeutigen Elementen gemäß einer Funktion zu filtern:
void main() {
List persons = List.of(
new Person("A"), new Person("B"), new Person("C"), new Person("A"),
new Person("D"), new Person("B")
);
List distinct =
persons.stream().gather(distinctBy(Person::getName)).toList();
// [Person[name=A], Person[name=B], Person[name=C], Person[name=D]]
System.out.println(distinct);
}
static Gatherer distinctBy(Function function) {
Gatherer.Integrator.Greedy, T, T> integrator =
(state, element, downstream) -> {
R functionResult = function.apply(element);
if (state.contains(functionResult)) {
return true;
} else {
state.add(functionResult);
return downstream.push(element);
}
};
return Gatherer.ofSequential(
HashSet::new,
Gatherer.Integrator.ofGreedy(integrator)
);
}
Der benutzerdefinierte Sammler verfolgt, ob ein bestimmter Wert bereits aufgrund des Ergebnisses angewendet einer Funktion auf jedes Streamelement zuvor aufgetreten ist. Wenn ein bestimmter Funktionswert noch nicht aufgetreten ist, wird das Streamelement stromabwärts gepusht.
Zugegeben, das ist isoliert eine recht große Menge Code, aber es handelt sich um eine wiederverwendbare Zwischenoperation für jeden Stream, der nur auf eindeutigen Elementen basierend auf einer bestimmten Funktion arbeiten muss.
Hier ist dieselbe Lösung, mit einem spezifischen Einmal-Sammler für das Szenario Person
:
List distinct = persons.stream().gather(Gatherer.ofSequential(
HashSet::new,
Gatherer.Integrator., Person, Person>ofGreedy(
(state, element, downstream) -> {
String name = element.getName();
if (state.contains(name)) {
return true;
} else {
state.add(name);
return downstream.push(element);
}
})
)).toList();
Javadocs
Gatherer
:
Eine Zwischenoperation, die einen Stream von Eingabeelementen in einen Stream von Ausgabeelementen umwandelt und optional eine abschließende Aktion anwendet, wenn das Ende des Upstreams erreicht wird. [...]
[…]
Es gibt viele Beispiele für Sammeloperationen, einschließlich, aber nicht beschränkt auf: Elemente in Chargen gruppieren (Fensterfunktionen); aufeinanderfolgende ähnliche Elemente deduplizieren; inkrementelle Akkumulationsfunktionen (Präfixscan); inkrementelle Funktionen zur Neuanordnung usw. Die Klasse Gatherers
bietet Implementierungen gängiger Sammeloperationen.
API-Hinweis:
Ein Gatherer
wird durch vier Funktionen definiert, die zusammenarbeiten, um Eingabeelemente zu verarbeiten, optional unter Verwendung eines Zwischenzustands und optional eine Abschlussaktion am Ende der Eingabe auszuführen. Sie sind:
Stream.gather(Gatherer gatherer)
:
Gibt einen Stream zurück, der aus den Ergebnissen der Anwendung des angegebenen Sammlers auf die Elemente dieses Streams besteht.
Gatherer.ofSequential(initializer, integrator)
Gibt einen neuen, sequentiellen Gatherer
zurück, der durch den angegebenen initializer
und integrator
beschrieben wird.