Wie kann ich das letzte Element eines Streams oder einer Liste im folgenden Code abrufen?
Wo data.careas
ist ein List<CArea>
:
CArea first = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal).findFirst().get();
CArea last = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.collect(Collectors.toList()).; //how to?
Wie Sie sehen können filter
, ist es nicht schwer , das erste Element mit einem bestimmten zu bekommen .
Das letzte Element in einem Einzeiler zu bekommen, ist jedoch ein echtes Problem:
- Es scheint, ich kann es nicht direkt von a erhalten
Stream
. (Es wäre nur für endliche Ströme sinnvoll) - Es scheint auch, dass Sie Dinge wie
first()
undlast()
von derList
Benutzeroberfläche nicht bekommen können, was wirklich ein Schmerz ist.
Ich sehe kein Argument dafür, dass in der Schnittstelle keine first()
und- last()
Methode angegeben List
wird, da die Elemente dort geordnet sind und außerdem die Größe bekannt ist.
Aber gemäß der ursprünglichen Antwort: Wie erhält man das letzte Element eines Endlichen Stream
?
Persönlich ist dies der nächste, den ich bekommen könnte:
int lastIndex = data.careas.stream()
.filter(c -> c.bbox.orientationHorizontal)
.mapToInt(c -> data.careas.indexOf(c)).max().getAsInt();
CArea last = data.careas.get(lastIndex);
Es beinhaltet jedoch die Verwendung eines indexOf
Elements für jedes Element, was Sie im Allgemeinen höchstwahrscheinlich nicht möchten, da es die Leistung beeinträchtigen kann.
quelle
Iterables.getLast
was Iterable nimmt, aber für die Arbeit optimiert istList
. Ein Haustier ärgern ist, dass es nicht hatgetFirst
. DieStream
API ist im Allgemeinen schrecklich anal und lässt viele bequeme Methoden aus. C # 's LINQ liefert gerne.Last()
und sogar.Last(Func<T,Boolean> predicate)
, obwohl es auch unendlich viele Enumerables unterstützt.Stream
API ist nicht vollständig vergleichbar mitLINQ
da beide in einem sehr unterschiedlichen Paradigma durchgeführt werden. Es ist nicht schlechter oder besser, es ist einfach anders. Und definitiv fehlen einige Methoden, nicht weil Orakelentwickler inkompetent oder gemein sind :)Antworten:
Es ist möglich, das letzte Element mit der Methode Stream :: redu zu erhalten . Die folgende Auflistung enthält ein minimales Beispiel für den allgemeinen Fall:
Diese Implementierung funktioniert für alle geordneten Streams (einschließlich aus Listen erstellter Streams ). Bei ungeordneten Streams ist aus offensichtlichen Gründen nicht angegeben, welches Element zurückgegeben wird.
Die Implementierung funktioniert sowohl für sequentielle als auch für parallele Streams . Das mag auf den ersten Blick überraschend sein, und in der Dokumentation wird dies leider nicht explizit angegeben. Es ist jedoch ein wichtiges Merkmal von Streams, und ich versuche es zu klären:
(first, second) -> second
.Die Dokumentation für die eng verwandten Collectors ist noch expliziter: "Um sicherzustellen, dass sequentielle und parallele Ausführungen erzeugen gleichwertige Ergebnisse erzielt werden , müssen die Kollektor Funktionen eine Identität erfüllen und eine Assoziativität Zwänge.“
Zurück zur ursprünglichen Frage: Der folgende Code speichert einen Verweis auf das letzte Element in der Variablen
last
und löst eine Ausnahme aus, wenn der Stream leer ist. Die Komplexität ist in der Länge des Stroms linear.quelle
_
oder eines ähnlichen), wenn Sie keinen Parameter benötigen? Also wäre:.reduce((_, current) -> current)
wenn nur das eine gültige Syntax ist..reduce(($, current) -> current)
oder.reduce((__, current) -> current)
(doppelter Unterstrich).Stream.reduce(BinaryOperator<T>)
wird nicht erwähnt, obreduce
die Reihenfolge der Begegnungen eingehalten wird, und bei einer Terminaloperation kann die Reihenfolge der Begegnungen ignoriert werden, selbst wenn der Stream bestellt wird. Abgesehen davon kommt das Wort "kommutativ" in den Stream-Javadocs nicht vor, daher sagt uns seine Abwesenheit nicht viel.reduce((a,b)->b)
es sich um eine korrekte Lösung handelt, um das letzte Element (natürlich eines geordneten Streams) zu erhalten, oder nicht. Die Aussage von Brian Goetz macht einen Punkt, weiter besagt die API-Dokumentation , dassreduce("", String::concat)
es sich um eine ineffiziente, aber korrekte Lösung für die Verkettung von Zeichenfolgen handelt, die die Aufrechterhaltung der Begegnungsreihenfolge impliziert. Die Absicht ist bekannt, die Dokumentation muss aufholen.Wenn Sie eine Sammlung (oder allgemeiner eine Iterable) haben, können Sie die von Google Guava verwenden
als praktischer Oneliner.
quelle
Iterables.getLast(() -> data.careas.stream().filter(c -> c.bbox.orientationHorizontal).iterator())
Ein Liner (kein Stream erforderlich;):
quelle
ArrayIndexOutOfBoundsException
.Guave hat eine spezielle Methode für diesen Fall:
Es ist gleichbedeutend mit,
stream.reduce((a, b) -> b)
aber die Entwickler behaupten, dass es eine viel bessere Leistung hat.Aus der Dokumentation :
Es ist erwähnenswert, dass sich diese Methode wie folgt verhält, wenn der Stream ungeordnet ist
findAny()
.quelle
Wenn Sie die letzten N Elemente erhalten müssen. Verschluss kann verwendet werden. Der folgende Code verwaltet eine externe Warteschlange mit fester Größe, bis der Stream das Ende erreicht.
Eine andere Option könnte darin bestehen, den Reduzierungsvorgang unter Verwendung der Identität als Warteschlange zu verwenden.
quelle
Sie können auch die Funktion skip () wie folgt verwenden ...
es ist super einfach zu bedienen.
quelle
ArrayIndexOutOfBoundsException