Allgemeine Frage: Was ist der richtige Weg, um einen Stream umzukehren? Angenommen, wir wissen nicht, aus welcher Art von Elementen dieser Stream besteht, wie kann ein Stream generisch umgekehrt werden?
Spezifische Frage:
IntStream
Bietet eine Bereichsmethode zum Generieren von Ganzzahlen in einem bestimmten Bereich IntStream.range(-range, 0)
. Jetzt, da ich den Umkehrbereich von 0 auf negativ umkehren möchte, funktioniert dies nicht mehr. Ich kann ihn auch nicht verwendenInteger::compare
List<Integer> list = Arrays.asList(1,2,3,4);
list.stream().sorted(Integer::compare).forEach(System.out::println);
mit IntStream
bekomme ich diesen compilerfehler
Fehler: (191, 0) ajc: Die Methode
sorted()
im TypIntStream
gilt nicht für die Argumente (Integer::compare
)
Was vermisse ich hier?
IntStream
hat keine.sorted(Comparator)
Methode; Sie müssen eine durchStream<Integer>
dort erste und umgekehrt eine vor nachgebendIntStream
IntStream.range(0, n)
in umgekehrter Reihenfolge zu generieren , gehen Sie wie folgt vormap(i -> n - i - 1)
. Boxen und Sortieren ist nicht erforderlich.1, 3, 2
Was ist Ihr erwartetes Ergebnis, wenn der Stream die Zahlen auf ungeordnete Weise erzeugt ? Möchten Sie den umgekehrten Stream wie2, 3, 1
oder den sortierten Stream wie3, 2, 1
?Antworten:
IntStream
Versuchen Sie für die spezielle Frage, eine Umkehrung zu generieren , Folgendes:Dies vermeidet Boxen und Sortieren.
Bei der allgemeinen Frage, wie ein Stream eines beliebigen Typs umgekehrt werden kann, weiß ich nicht, dass es einen "richtigen" Weg gibt. Ich kann mir ein paar Möglichkeiten vorstellen. Beide speichern am Ende die Stream-Elemente. Ich kenne keine Möglichkeit, einen Stream umzukehren, ohne die Elemente zu speichern.
Auf diese erste Weise werden die Elemente in einem Array gespeichert und in umgekehrter Reihenfolge in einen Stream ausgelesen. Da wir den Laufzeittyp der Stream-Elemente nicht kennen, können wir das Array nicht richtig eingeben, was eine ungeprüfte Umwandlung erfordert.
Bei einer anderen Technik werden die Elemente mithilfe von Sammlern in einer umgekehrten Liste zusammengefasst. Dadurch werden viele Einfügungen an der Vorderseite von
ArrayList
Objekten vorgenommen, sodass viel kopiert wird.Es ist wahrscheinlich möglich, einen viel effizienteren Umkehrkollektor mit einer angepassten Datenstruktur zu schreiben.
UPDATE 29.01.2016
Da diese Frage in letzter Zeit etwas Beachtung gefunden hat, sollte ich meine Antwort aktualisieren, um das Problem mit dem Einfügen vorne zu lösen
ArrayList
. Dies ist bei einer großen Anzahl von Elementen schrecklich ineffizient und erfordert das Kopieren von O (N ^ 2).Es ist vorzuziehen,
ArrayDeque
stattdessen ein zu verwenden, das das Einsetzen an der Vorderseite effizient unterstützt. Eine kleine Falte ist, dass wir die Drei-Argumente-Form von nicht verwenden könnenStream.collect()
; Es erfordert, dass der Inhalt des zweiten Args mit dem ersten Arg zusammengeführt wird, und es gibt keine Massenoperation "Alles an der Vorderseite hinzufügen"Deque
. StattdessenaddAll()
hängen wir den Inhalt des ersten Arguments an das Ende des zweiten an und geben dann das zweite zurück. Dies erfordert die Verwendung derCollector.of()
Factory-Methode.Der vollständige Code lautet:
Das Ergebnis ist ein
Deque
statt einesList
, aber das sollte kein großes Problem sein, da es leicht in der jetzt umgekehrten Reihenfolge iteriert oder gestreamt werden kann.quelle
IntStream.iterate(to-1, i->i-1).limit(to-from)
.limit(endExcl-(long)startIncl)
stattdessen verwenden, aber für solch große Streams wird es sowieso sehr entmutigt, da es viel weniger effizient ist als dierange
basierte Lösung. Als ich den Kommentar schrieb, war mir der Effizienzunterschied nicht bewusst.Elegante Lösung
quelle
Comparable
...Viele der Lösungen hier sortieren oder kehren das um
IntStream
, aber das erfordert unnötig Zwischenspeicher. Die Lösung von Stuart Marks ist der richtige Weg:Es behandelt auch den Überlauf korrekt und besteht diesen Test:
quelle
Estreams
Namen zu veröffentlichen (ich werde ihn aus dem Beitrag entfernen). Es ist eines unserer unternehmensinternen Utility - Klassen, die wir ergänzen verwendenjava.util.stream.Stream
‚sstatic
Methoden.StreamEx
indem Sie SchrittIntStreamEx.rangeClosed(from-1, to, -1)
Allgemeine Frage:
Stream speichert keine Elemente.
Das Iterieren von Elementen in umgekehrter Reihenfolge ist daher nicht möglich, ohne die Elemente in einer Zwischensammlung zu speichern.
Update: LinkedList in ArrayDeque geändert (besser) siehe hier für Details
Drucke:
Übrigens ist die Verwendung der
sort
Methode nicht korrekt, da sie sortiert und NICHT umgekehrt wird (vorausgesetzt, der Stream enthält möglicherweise ungeordnete Elemente).Spezifische Frage:
Ich fand das einfach, einfacher und intuitiver (kopierter @ Holger- Kommentar )
quelle
sorted
unddistinct
speichern tatsächlich ein Zwischenergebnis. Informationen dazu finden Sie in den Paket-API-Dokumenten .No storage
auf der gleichen Seite. Sogar es speichert, dass wir keinen Zugriff auf diesen Speicher bekommen können (alsoNo storage
ist es in Ordnung, denke ich)ohne externe lib ...
quelle
Wenn umgesetzt
Comparable<T>
(ex.Integer
,String
,Date
), Können Sie es mit tunComparator.reverseOrder()
.quelle
Stream.of(1,3,2)
das Ergebnis hätten, wäreStream.of(3,2,1)
NICHTStream.of(2,3,1)
Sie können Ihren eigenen Kollektor definieren, der die Elemente in umgekehrter Reihenfolge sammelt:
Und benutze es wie:
Ich verwende eine ArrayList in Vorwärtsreihenfolge, um die Elemente (am Ende der Liste) effizient einzufügen, und Guava Lists.reverse, um eine effiziente umgekehrte Ansicht der Liste zu erstellen, ohne eine weitere Kopie davon zu erstellen.
Hier sind einige Testfälle für den benutzerdefinierten Sammler:
quelle
CyclU - React StreamUtils verfügt über eine Reverse-Stream-Methode ( Javadoc ).
Es funktioniert, indem es in einer ArrayList gesammelt und dann die ListIterator-Klasse verwendet wird, die in beide Richtungen iterieren kann, um rückwärts über die Liste zu iterieren.
Wenn Sie bereits eine Liste haben, ist diese effizienter
quelle
Ich würde vorschlagen, jOOλ zu verwenden . Es ist eine großartige Bibliothek, die Java 8-Streams und Lambdas viele nützliche Funktionen hinzufügt.
Sie können dann Folgendes tun:
So einfach ist das. Es ist eine ziemlich leichte Bibliothek und es lohnt sich, sie zu jedem Java 8-Projekt hinzuzufügen.
quelle
Hier ist die Lösung, die ich mir ausgedacht habe:
dann mit diesen Komparatoren:
quelle
Collections.reverseOrder()
existiert seit Java 1.2 und arbeitet mitInteger
…Wie wäre es mit dieser Dienstprogrammmethode?
Scheint mit allen Fällen ohne Duplizierung zu arbeiten.
quelle
quelle
Einfachster Weg (einfaches Sammeln - unterstützt parallele Streams):
Erweiterte Methode (unterstützt fortlaufend parallele Streams):
Beachten Sie, dass Sie schnell auf andere Arten von Streams (IntStream, ...) erweitern können.
Testen:
Ergebnisse:
Zusätzliche Hinweise: Das
simplest way
ist nicht so nützlich, wenn es mit anderen Stream-Operationen verwendet wird (der Collect Join unterbricht die Parallelität). Dasadvance way
hat dieses Problem nicht, und es behält zum Beispiel auch die anfänglichen Eigenschaften des Streams beiSORTED
, und so ist es der Weg, um nach dem Umkehren mit anderen Stream-Operationen zu arbeiten.quelle
Man könnte einen Kollektor schreiben, der Elemente in umgekehrter Reihenfolge sammelt:
Und benutze es so:
Ursprüngliche Antwort (enthält einen Fehler - funktioniert nicht ordnungsgemäß für parallele Streams):
Eine Allzweck-Stream-Reverse-Methode könnte folgendermaßen aussehen:
quelle
Nicht nur Java8, aber wenn Sie die Lists.reverse () -Methode von guava in Verbindung verwenden, können Sie dies leicht erreichen:
quelle
In Bezug auf die spezifische Frage der Erzeugung einer Umkehrung
IntStream
:Ab Java 9 können Sie die Version mit drei Argumenten verwenden für
IntStream.iterate(...)
:wo:
IntStream.iterate(int seed, IntPredicate hasNext, IntUnaryOperator next);
seed
- das Anfangselement;hasNext
- ein Prädikat, das auf Elemente angewendet werden soll, um zu bestimmen, wann der Stream beendet werden muss;next
- Eine Funktion, die auf das vorherige Element angewendet wird, um ein neues Element zu erzeugen.quelle
Als Referenz habe ich mir das gleiche Problem angesehen, ich wollte den String-Wert von Stream-Elementen in umgekehrter Reihenfolge verbinden.
itemList = {last, middle, first} => first, middle, last
Ich fing an, eine Zwischensammlung mit
collectingAndThen
von comonad oder demArrayDeque
Sammler von Stuart Marks zu verwenden , obwohl ich mit der Zwischensammlung nicht zufrieden war und wieder StreamingAlso habe ich die Antwort von Stuart Marks wiederholt, die die
Collector.of
Fabrik benutzte , die den interessanten Finisher Lambda hat.Da in diesem Fall der Stream nicht parallel ist, ist der Kombinierer nicht so relevant, wie ich verwende
insert
aus Gründen der Codekonsistenz trotzdem, aber es spielt keine Rolle, da dies davon abhängt, welcher Stringbuilder zuerst erstellt wird.Ich habe mir den StringJoiner angesehen, aber er hat keine
insert
Methode.quelle
Die Beantwortung einer bestimmten Frage zum Rückgängigmachen mit IntStream hat für mich funktioniert:
quelle
ArrayDeque
sind schneller im Stapel als ein Stapel oder eine verknüpfte Liste. "push ()" fügt Elemente an der Vorderseite des Deque einquelle
Zeichenfolge oder Array umkehren
Die Aufteilung kann basierend auf dem Trennzeichen oder Leerzeichen geändert werden
quelle
Die einfachste Lösung ist die Verwendung von
List::listIterator
undStream::generate
quelle
Stream.generate()
in unendlichen Stream generiert wird, daher ist der Aufruf vonlimit()
hier sehr wichtig.So mache ich es.
Ich mag die Idee nicht, eine neue Sammlung zu erstellen und sie rückgängig zu machen.
Die IntStream # -Kartenidee ist ziemlich ordentlich, aber ich bevorzuge die IntStream # -Iterationsmethode, da ich denke, dass die Idee eines Countdowns bis Null mit der Iterationsmethode besser ausgedrückt und leichter zu verstehen ist, wenn das Array von hinten nach vorne verschoben wird.
Hier sind einige Tests, um zu beweisen, dass es funktioniert:
quelle
In all dem sehe ich nicht die Antwort, zu der ich zuerst gehen würde.
Dies ist nicht gerade eine direkte Antwort auf die Frage, aber es ist eine mögliche Lösung für das Problem.
Erstellen Sie die Liste zunächst rückwärts. Wenn Sie können, verwenden Sie eine LinkedList anstelle einer ArrayList. Wenn Sie Elemente hinzufügen, verwenden Sie "Push" anstelle von "Hinzufügen". Die Liste wird in umgekehrter Reihenfolge erstellt und dann ohne Manipulation korrekt gestreamt.
Dies passt nicht zu Fällen, in denen Sie mit primitiven Arrays oder Listen arbeiten, die bereits auf verschiedene Weise verwendet werden, aber in überraschend vielen Fällen gut funktionieren.
quelle
Diese Methode funktioniert mit jedem Stream und ist Java 8-kompatibel:
quelle
Der allgemeinste und einfachste Weg, eine Liste umzukehren, ist:
quelle
Comparator
. Daher kann Ihnen niemand garantieren, dass dieser "Trick" in jeder zukünftigen Java-Version mit einem beliebigen Sortieralgorithmus funktioniert. Der gleiche Trick funktioniert beispielsweise nicht für parallele Streams, da der parallele SortieralgorithmusComparator
auf unterschiedliche Weise verwendet wird. Bei der sequentiellen Sortierung funktioniert dies rein zufällig. Ich würde niemandem empfehlen, diese Lösung zu verwenden.System.setProperty("java.util.Arrays.useLegacyMergeSort", "true");
public static <T> void reverseHelper(List<T> li){ li.parallelStream() .sorted((x,y)->-1) .collect(Collectors.toList()) .forEach(System.out::println); }
reverseHelper(IntStream.range(0, 8193).boxed().collect(Collectors.toList()))
(das Ergebnis kann jedoch von der Anzahl der Kerne abhängen).Java 8 Weg, dies zu tun:
quelle