Bei einem Stream wie { 0, 1, 2, 3, 4 }
,
Wie kann ich es am elegantesten in eine gegebene Form bringen:
{ new Pair(0, 1), new Pair(1, 2), new Pair(2, 3), new Pair(3, 4) }
(vorausgesetzt natürlich, ich habe das Klassenpaar definiert)?
Bearbeiten: Hier geht es nicht ausschließlich um Ints oder primitive Streams. Die Antwort sollte für einen Stream jeglicher Art allgemein sein.
java
java-8
java-stream
Aleksandr Dubinsky
quelle
quelle
list.stream().map(i -> new Pair(i, i+1));
Map.Entry
als Pair-Klasse. (Zugegeben, einige mögen das für einen Hack halten, aber die Verwendung einer eingebauten Klasse ist praktisch.)Antworten:
Meine StreamEx- Bibliothek, die Standard-Streams erweitert, bietet eine
pairMap
Methode für alle Stream-Typen. Bei primitiven Streams wird der Stream-Typ nicht geändert, es können jedoch einige Berechnungen durchgeführt werden. Am häufigsten werden Unterschiede berechnet:Für den Objektstrom können Sie einen beliebigen anderen Objekttyp erstellen. Meine Bibliothek bietet keine neuen vom Benutzer sichtbaren Datenstrukturen wie
Pair
(das ist der Teil des Bibliothekskonzepts). Wenn Sie jedoch eine eigenePair
Klasse haben und diese verwenden möchten, können Sie Folgendes tun:Oder wenn Sie bereits welche haben
Stream
:Diese Funktionalität wird mithilfe eines benutzerdefinierten Spliterators implementiert . Es hat einen recht geringen Overhead und kann gut parallelisiert werden. Natürlich funktioniert es mit jeder Stream-Quelle, nicht nur mit einer Direktzugriffsliste / einem Array wie vielen anderen Lösungen. In vielen Tests funktioniert es sehr gut. Hier ist ein JMH-Benchmark, bei dem wir alle Eingabewerte vor einem größeren Wert mit unterschiedlichen Ansätzen finden (siehe diese Frage).
quelle
StreamEx
implementiertIterable
! Hurra!)Stream
in eine einwickeln, um Ihre Antwort zu 100% zu vervollständigenStreamEx
?StreamEx.of(stream)
. Es gibt andere praktische statische Methoden zum Erstellen des StreamsCollection
, ArraysReader
usw. Die Antwort wurde bearbeitet.pairMap
in sequentiellen Streams bestellt? Eigentlich hätte ich gerne forPairsOrdered (), aber da es keine solche Methode gibt, kann ich sie irgendwie simulieren?stream.ordered().forPairs()
oderstream().pairMap().forEachOrdered()
?pairMap
ist die Zwischenoperation mit nicht störender zustandsloser Mapper-Funktion, deren Reihenfolge nicht wie bei einfach angegeben wirdmap
. DasforPairs
ist nach Spezifikation ungeordnet, aber ungeordnete Operationen sind de facto für sequentielle Streams geordnet. Es wäre schön, wenn Sie Ihr ursprüngliches Problem als separate Stackoverflow-Frage formulieren würden, um mehr Kontext bereitzustellen.Die Java 8-Streams-Bibliothek ist in erster Linie darauf ausgerichtet, Streams für die parallele Verarbeitung in kleinere Blöcke aufzuteilen. Daher sind Stateful-Pipeline-Phasen sehr begrenzt, und beispielsweise das Abrufen des Index des aktuellen Stream-Elements und der Zugriff auf benachbarte Stream-Elemente werden nicht unterstützt.
Ein typischer Weg, um diese Probleme mit einigen Einschränkungen zu lösen, besteht natürlich darin, den Stream durch Indizes zu steuern und sich darauf zu verlassen, dass die Werte in einer Datenstruktur mit wahlfreiem Zugriff wie einer ArrayList verarbeitet werden, aus der die Elemente abgerufen werden können. Wenn die Werte in
arrayList
wären, könnte man die Paare wie gewünscht erzeugen, indem man so etwas macht:Die Einschränkung besteht natürlich darin, dass die Eingabe kein unendlicher Strom sein kann. Diese Pipeline kann jedoch parallel ausgeführt werden.
quelle
arrayList
) ist in der Tat eine Sammlung, weshalb ich sie nicht als Antwort markiert habe. (Aber herzlichen Glückwunsch zu Ihrem Goldabzeichen!)Dies ist nicht elegant, es ist eine hackige Lösung, funktioniert aber für unendliche Streams
Jetzt können Sie Ihren Stream auf die gewünschte Länge beschränken
PS Ich hoffe, es gibt eine bessere Lösung, so etwas wie Clojure
(partition 2 1 stream)
quelle
parallelStream
Dokument: "Um korrektes Verhalten zu bewahren, müssen diese Verhaltensparameter nicht störend und in den meisten Fällen zustandslos sein"Ich habe einen spliterator Wrapper implementiert , die alle nimmt
n
ElementeT
aus dem ursprünglichen spliterator und produziertList<T>
:Die folgende Methode kann verwendet werden, um einen aufeinanderfolgenden Stream zu erstellen:
Beispielnutzung:
quelle
List<E>
Elementen erstellt. Jede Liste enthältn
aufeinanderfolgende Elemente aus dem ursprünglichen Stream. Überprüfen Sie es selbst;)(partition size step)
Funktion, und dies ist der beste Weg, um sie zu erhalten.ArrayDeque
für die Leistung vorLinkedList
.Sie können dies mit der Stream.reduce () -Methode tun (ich habe keine anderen Antworten mit dieser Technik gesehen).
quelle
Sie können dies in Cyclops-React (ich trage zu dieser Bibliothek bei) mit dem Sliding-Operator tun .
Oder
Angenommen, der Pair-Konstruktor kann eine Sammlung mit 2 Elementen akzeptieren.
Wenn Sie um 4 gruppieren und um 2 erhöhen möchten, wird dies ebenfalls unterstützt.
Entsprechende statische Methoden zum Erstellen einer verschiebbaren Ansicht über java.util.stream.Stream werden auch in der StreamUtils- Klasse von cyclops-Streams bereitgestellt .
Hinweis: - Für den Single-Thread-Betrieb wäre ReactiveSeq besser geeignet. LazyFutureStream erweitert ReactiveSeq, ist jedoch hauptsächlich auf die gleichzeitige / parallele Verwendung ausgerichtet (es handelt sich um einen Stream of Futures).
LazyFutureStream erweitert ReactiveSeq, wodurch Seq vom fantastischen jOOλ (das java.util.stream.Stream erweitert) erweitert wird, sodass die von Lukas vorgestellten Lösungen auch mit beiden Stream-Typen funktionieren würden. Für alle Interessierten sind die Hauptunterschiede zwischen den Fenster- / Gleitoperatoren der offensichtliche relative Kompromiss zwischen Leistung und Komplexität und die Eignung für die Verwendung mit unendlichen Streams (das Gleiten verbraucht nicht den Stream, sondern puffert, während er fließt).
quelle
Die Protonenpack-Bibliothek bietet die Fensterfunktionalität. Bei einer Pair-Klasse und einem Stream können Sie dies folgendermaßen tun:
Jetzt
pairs
enthält der Stream:quelle
st
zweimal erstellen ! Kann diese Bibliothek das Problem mit einem einzigen Stream lösen?windowed
Funktionalität wurde hinzugefügt! Siehe die Bearbeitung.Aufeinanderfolgende Paare finden
Wenn Sie bereit sind, eine Bibliothek eines Drittanbieters zu verwenden und keine Parallelität benötigen, bietet jOOλ folgende Fensterfunktionen im SQL-Stil
Nachgeben
Die
lead()
Funktion greift über das Fenster auf den nächsten Wert in Durchlaufreihenfolge zu.Aufeinanderfolgende Tripel / Vierfache / n-Tupel finden
Eine Frage in den Kommentaren war die Frage nach einer allgemeineren Lösung, bei der nicht Paare, sondern n-Tupel (oder möglicherweise Listen) gesammelt werden sollten. Hier ist also ein alternativer Ansatz:
Eine Liste von Listen erstellen
Ohne das
filter(w -> w.count() == n)
wäre das ErgebnisHaftungsausschluss: Ich arbeite für die Firma hinter jOOλ
quelle
w.lead().lead()
?tuple(w.value(), w.lead(1), w.lead(2))
wäre eine Option. Ich habe meine Antwort mit einer allgemeineren Lösung fürlength = n
.window()
keine verzögerte Operation ist, bei der der gesamte Eingabestream in einer Zwischensammlung gesammelt und dann ein neuer Stream daraus erstellt wird.Comparator
wird Neuordnungs Fenster verwendet wird ), dann eine Optimierung wie dies möglich wäre, und wird wahrscheinlich in Zukunft umgesetzt werden.Streams.zip(..)
ist in Guave für diejenigen verfügbar , die davon abhängig sind.Beispiel:
quelle
Wir können RxJava (sehr leistungsfähige reaktive Erweiterungsbibliothek ) verwenden.
quelle
Observable.zip(obs, obs.skip(1), pair->{...})
bis jetzt verwendet! Ich wusste nicht, dassObservable.buffer
es eine Version mit einem Schritt gibt (und ich bin an denzip
Trick von Python gewöhnt). +1Die Operation ist im Wesentlichen zustandsbehaftet, daher nicht wirklich, welche Streams gelöst werden sollen - siehe Abschnitt "Statusloses Verhalten" im Javadoc :
Eine Lösung besteht darin, den Status in Ihren Stream über einen externen Zähler einzuführen, obwohl dies nur mit einem sequentiellen Stream funktioniert.
quelle
Stream
! = "Lambdas".StreamEx
Bibliothek ist auch ein guter Fund und könnte eine Antwort für sich sein. Mein Kommentar zu "Streams! = Lambdas" bezieht sich auf Sie mit der Aussage "Die Operation ist im Wesentlichen zustandsbehaftet, also nicht wirklich, was Lambdas lösen sollen." Ich denke, Sie wollten das Wort "Streams" verwenden.In Ihrem Fall würde ich meine benutzerdefinierte IntFunction schreiben, die den zuletzt übergebenen int verfolgt, und diesen verwenden, um den ursprünglichen IntStream zuzuordnen.
quelle
Für aufeinanderfolgende Unterschiede in der Zeit (x-Werte) ein zeitseriellen Berechnung verwende ich die
stream
‚s -collect(...)
Methode:Wo der DifferenceCollector so etwas ist:
Sie könnten dies wahrscheinlich an Ihre Bedürfnisse anpassen.
quelle
Ich habe endlich einen Weg gefunden, den Stream.reduce auszutricksen, um ordentlich mit Wertepaaren umgehen zu können. Es gibt eine Vielzahl von Anwendungsfällen, für die diese Funktion erforderlich ist, die in JDK 8 nicht selbstverständlich vorkommen :
Der Trick, den ich benutze, ist das Rückgaberecht; Aussage.
quelle
reduce
gibt keine ausreichenden Garantien dafür, dass dies funktioniert.Eine elegante Lösung wäre die Verwendung eines Reißverschlusses . Etwas wie:
Dies ist ziemlich prägnant und elegant, verwendet jedoch eine Liste als Eingabe. Eine unendliche Stream-Quelle kann auf diese Weise nicht verarbeitet werden.
Ein weiteres (viel problematischeres) Problem ist, dass zip zusammen mit der gesamten Streams-Klasse kürzlich aus der API entfernt wurde. Der obige Code funktioniert nur mit b95 oder älteren Versionen. Mit dem neuesten JDK würde ich also sagen, dass es keine elegante Lösung im FP-Stil gibt, und im Moment können wir nur hoffen, dass zip auf irgendeine Weise wieder in die API eingeführt wird.
quelle
zip
wurde entfernt. Ich erinnere mich nicht an alles, was in derStreams
Klasse war, aber einige Dinge wurden zu statischen Methoden auf derStream
Schnittstelle migriert , und es gibt auchStreamSupport
undStream.Builder
Klassen.zip
? Welcher pedantische Grund auch immer erfunden werden mag, rechtfertigt nicht das Tötenzip
.Dies ist ein interessantes Problem. Ist mein Hybridversuch unter irgendetwas gut?
Ich glaube, es eignet sich nicht für die parallele Verarbeitung und kann daher disqualifiziert werden.
quelle
Stream
, keineList
. Natürlich können wir auch einen Iterator aus einem Stream herausholen, daher ist dies möglicherweise eine gültige Lösung. Trotzdem ist es ein origineller Ansatz.Wie andere beobachtet haben, ist aufgrund der Art des Problems eine gewisse Staatlichkeit erforderlich.
Ich war mit einem ähnlichen Problem konfrontiert, bei dem ich im Wesentlichen die Oracle SQL-Funktion LEAD haben wollte. Mein Versuch, das umzusetzen, ist unten.
quelle
Sie können dies erreichen, indem Sie eine begrenzte Warteschlange verwenden, um Elemente zu speichern, die durch den Stream fließen (was auf der Idee basiert, die ich hier ausführlich beschrieben habe: Ist es möglich, das nächste Element im Stream abzurufen? )
Das folgende Beispiel definiert zuerst die Instanz der BoundedQueue-Klasse, in der Elemente gespeichert werden, die durch den Stream gehen (wenn Sie die Idee zur Erweiterung der LinkedList nicht mögen, lesen Sie den oben genannten Link für einen alternativen und allgemeineren Ansatz). Später kombinieren Sie einfach zwei aufeinanderfolgende Elemente zu einer Instanz von Pair:
quelle
Ich stimme @aepurniet zu, aber stattdessen müssen Sie mapToObj verwenden
quelle
Führen Sie eine
for
Schleife aus, die von 0 bislength-1
zu Ihrem Stream läuftquelle