Gibt es eine übersichtliche Möglichkeit, einen Stream zu durchlaufen, während Sie Zugriff auf den Index im Stream haben?
String[] names = {"Sam","Pamela", "Dave", "Pascal", "Erik"};
List<String> nameList;
Stream<Integer> indices = intRange(1, names.length).boxed();
nameList = zip(indices, stream(names), SimpleEntry::new)
.filter(e -> e.getValue().length() <= e.getKey())
.map(Entry::getValue)
.collect(toList());
Das scheint ziemlich enttäuschend im Vergleich zu dem dort gegebenen LINQ-Beispiel
string[] names = { "Sam", "Pamela", "Dave", "Pascal", "Erik" };
var nameList = names.Where((c, index) => c.Length <= index + 1).ToList();
Gibt es einen prägnanteren Weg?
Weiter scheint es, dass der Reißverschluss entweder bewegt oder entfernt wurde ...
java
java-8
java-stream
Graeme Moss
quelle
quelle
intRange()
? Ich bin bisher noch nicht auf diese Methode in Java 8 gestoßen.IntStream.rangeClosed(x, y)
.List<String> allCities = map.values().stream().flatMap(list -> list.stream()).collect(Collectors.toList());
zip
wurde entfernt, zusammen mit experimentellen zweiwertigen Streams, die unterschiedlich alsBiStream
oder bezeichnet wurdenMapStream
. Das Hauptproblem besteht darin, dass Java, um dies effektiv zu tun, wirklich einen strukturell typisierten Paartyp (oder Tupeltyp) benötigt. Fehlt eine, ist es einfach, eine generische Paar- oder Tupelklasse zu erstellen - dies wurde schon oft durchgeführt -, aber alle werden auf den gleichen Typ gelöscht.Antworten:
Der sauberste Weg ist, von einem Strom von Indizes auszugehen:
Die resultierende Liste enthält nur "Erik".
Eine Alternative , die besser vertraut aussieht , wenn Sie verwendet werden , um für Schleifen würden ein Ad - hoc - Zähler unter Verwendung eines veränderliches Objekt zu halten, zum Beispiel ein
AtomicInteger
:Beachten Sie, dass die Verwendung der letzteren Methode in einem parallelen Stream unterbrochen werden kann, da die Elemente nicht unbedingt "in der richtigen Reihenfolge" verarbeitet werden müssen .
quelle
public static <T> Stream<Tuple2<Integer, T>> zipWithIndex(Stream<T> stream) { final AtomicInteger index = new AtomicInteger(); final Function<T, Tuple2<Integer, T>> zipper = e -> Tuples.of(index.getAndIncrement(), e); if (stream.isParallel()) { return stream.sequential().map(zipper).parallel(); } else { return stream.map(zipper); } }
parallel
odersequential
wird geehrt, wenn der Terminalbetrieb beginnt.Der Java 8-Streams-API fehlen die Funktionen zum Abrufen des Index eines Stream-Elements sowie die Möglichkeit, Streams zusammen zu komprimieren. Dies ist bedauerlich, da dadurch bestimmte Anwendungen (wie die LINQ-Herausforderungen) schwieriger werden als sonst.
Es gibt jedoch häufig Problemumgehungen. Normalerweise kann dies erreicht werden, indem der Stream mit einem ganzzahligen Bereich "angesteuert" wird und die Tatsache ausgenutzt wird, dass sich die ursprünglichen Elemente häufig in einem Array oder in einer Sammlung befinden, auf die über den Index zugegriffen werden kann. Zum Beispiel kann das Challenge 2-Problem folgendermaßen gelöst werden:
Wie oben erwähnt, nutzt dies die Tatsache aus, dass die Datenquelle (das Namensarray) direkt indizierbar ist. Wenn es nicht wäre, würde diese Technik nicht funktionieren.
Ich gebe zu, dass dies nicht die Absicht von Herausforderung 2 erfüllt. Trotzdem löst es das Problem einigermaßen effektiv.
BEARBEITEN
In meinem vorherigen Codebeispiel wurden
flatMap
die Filter- und Kartenoperationen zusammengeführt, dies war jedoch umständlich und bot keinen Vorteil. Ich habe das Beispiel gemäß dem Kommentar von Holger aktualisiert.quelle
IntStream.range(0, names.length).filter(i->names[i].length()<=i).mapToObj(i->names[i])
? Es funktioniert ohne Boxen…flatMap
trotzdem benutzen ?flatMap
weil es eine Filter- und Zuordnungsoperation zu einer einzigen Operation zusammenführt, aber das bietet wirklich keinen Vorteil. Ich werde das Beispiel bearbeiten.Stream.of( names ).filter( n -> n.length() <= 1).collect( Collectors.toList() );
weniger Unboxing und weniger Speicherzuweisung; da wir keinen Range Stream mehr erstellen.Seit Guave 21 können Sie verwenden
Beispiel (aus dem offiziellen Dokument ):
quelle
Ich habe in meinem Projekt die folgende Lösung verwendet. Ich denke, es ist besser als veränderbare Objekte oder ganzzahlige Bereiche zu verwenden.
quelle
StreamSupport.stream()
und einen benutzerdefinierten Iterator "einfügt" .Zusätzlich zum Protonpack bietet Seq von jOOλ diese Funktionalität (und durch Erweiterungsbibliotheken, die darauf aufbauen, wie Zyklopen reagieren , bin ich der Autor dieser Bibliothek).
Seq unterstützt auch nur Seq.of (Namen) und erstellt einen JDK-Stream unter der Decke.
Das einfach reagierende Äquivalent würde ähnlich aussehen
Die einfach reagierende Version ist eher auf asynchrone / gleichzeitige Verarbeitung zugeschnitten.
quelle
Der Vollständigkeit halber ist hier die Lösung für meine StreamEx- Bibliothek:
Hier erstellen wir eine,
EntryStream<Integer, String>
dieStream<Entry<Integer, String>>
einige spezifische Operationen wiefilterKeyValue
oder erweitert und hinzufügtvalues
. Es wird auch einetoList()
Verknüpfung verwendet.quelle
.forEach(entry -> {})
?.forKeyValue((key, value) -> {})
.Ich habe die Lösungen hier gefunden, wenn der Stream aus einer Liste oder einem Array erstellt wird (und Sie die Größe kennen). Aber was ist, wenn Stream eine unbekannte Größe hat? In diesem Fall versuchen Sie diese Variante:
Verwendungszweck:
quelle
Mit einer Liste können Sie versuchen
Ausgabe:
quelle
Es gibt keine Möglichkeit, über eine Weile zu iterieren,
Stream
während Sie Zugriff auf den Index haben, da aStream
anders ist als alle anderenCollection
. AStream
ist lediglich eine Pipeline zum Übertragen von Daten von einem Ort zum anderen, wie in der Dokumentation angegeben :Kein Speicher. Ein Stream ist keine Datenstruktur, in der Elemente gespeichert werden. Stattdessen übertragen sie Werte von einer Quelle (die eine Datenstruktur, ein Generator, ein E / A-Kanal usw. sein kann) durch eine Pipeline von Rechenoperationen.
Natürlich können Sie, wie Sie in Ihrer Frage anscheinend andeuten, Ihre jederzeit
Stream<V>
in eine konvertierenCollection<V>
, z. B. eineList<V>
, in der Sie Zugriff auf die Indizes haben.quelle
Mit https://github.com/poetix/protonpack können Sie diese Zip-Datei erstellen:
quelle
Wenn Sie nichts dagegen haben eine Drittanbieter - Bibliothek, Eclipse - Kollektionen haben
zipWithIndex
undforEachWithIndex
verfügbar für den Einsatz in vielen Arten. Hier finden Sie eine Reihe von Lösungen für diese Herausforderung, die sowohl für JDK-Typen als auch für Eclipse-Sammlungstypen verwendet werdenzipWithIndex
.Hier ist eine Lösung, die
forEachWithIndex
stattdessen verwendet wird.Wenn Sie die Lambdas oben in anonyme innere Klassen ändern, funktionieren alle diese Codebeispiele auch in Java 5 - 7.
Hinweis: Ich bin ein Committer für Eclipse-Sammlungen
quelle
Wenn Sie zufällig Vavr (früher bekannt als Javaslang) verwenden, können Sie die dedizierte Methode nutzen:
Wenn wir den Inhalt ausdrucken, werden wir etwas Interessantes sehen:
Dies liegt daran, dass
Streams
wir faul sind und keine Ahnung haben, welche Elemente im Stream als nächstes angezeigt werden.quelle
Wenn Sie versuchen, einen Index basierend auf einem Prädikat zu erhalten, versuchen Sie Folgendes:
Wenn Sie sich nur für den ersten Index interessieren:
Oder wenn Sie mehrere Indizes suchen möchten:
Fügen
.orElse(-1);
Sie hinzu, falls Sie einen Wert zurückgeben möchten, wenn er nicht gefunden wird.quelle
Hier ist Code von AbacusUtil
Offenlegung: Ich bin der Entwickler von AbacusUtil.
quelle
Sie können verwenden
IntStream.iterate()
, um den Index zu erhalten:Dies funktioniert nur für Java 9 ab Java 8. Sie können Folgendes verwenden:
quelle
Sie können eine statische innere Klasse erstellen, um den Indexer wie im folgenden Beispiel zu kapseln:
quelle
Diese Frage ( Stream Way zum Abrufen des Index des ersten Elements, das mit dem Booleschen Wert übereinstimmt ) hat die aktuelle Frage als Duplikat markiert, sodass ich sie dort nicht beantworten kann. Ich beantworte es hier.
Hier ist eine generische Lösung, um den passenden Index zu erhalten, für den keine externe Bibliothek erforderlich ist.
Wenn Sie eine Liste haben.
Und nenne es so:
Und wenn Sie eine Sammlung verwenden, probieren Sie diese aus.
quelle
Eine Möglichkeit besteht darin, jedes Element im Flow zu indizieren:
Die Verwendung einer anonymen Klasse entlang eines Streams wird nicht gut verwendet, ist aber sehr nützlich.
quelle
Sie brauchen nicht zu einem
map
notwendigerweise, die am nächsten Lambda zum LINQ Beispiel ist:
quelle
AUSGABE: Sam, Pamela, Dave, Pascal, Erik
So sammeln Sie in der Liste:
quelle
List
die Lösung der obigen Frage ein Element enthält, das Erik enthält .Wie jean-baptiste-yunès sagte, wenn Ihr Stream auf einer Java-Liste basiert, ist die Verwendung einer AtomicInteger und ihrer incrementAndGet-Methode eine sehr gute Lösung für das Problem, und die zurückgegebene Ganzzahl entspricht dem Index in der ursprünglichen Liste, solange Sie Verwenden Sie keinen parallelen Stream.
quelle
Wenn Sie den Index in forEach benötigen, bietet dies eine Möglichkeit.
Verwenden Sie es dann wie folgt.
quelle