Stream Methode, um den Index des ersten Elements zu erhalten, das mit dem booleschen Wert übereinstimmt
Lesezeit: 5 Minuten
Ich habe ein List<Users>. Ich möchte den Index des (ersten) Benutzers im Stream mit einem bestimmten Benutzernamen erhalten. Ich möchte das eigentlich nicht verlangen User sein .equals() zu einigen beschrieben Usernur um den gleichen Benutzernamen zu haben.
Ich kann mir hässliche Möglichkeiten vorstellen, dies zu tun (Iterate und Count), aber es scheint, als ob es eine nette Möglichkeit geben sollte, dies zu tun, wahrscheinlich durch die Verwendung von Streams. Das Beste, was ich bisher habe, ist:
int index = users.stream()
.map(user -> user.getName())
.collect(Collectors.toList())
.indexOf(username);
Das ist nicht der schlechteste Code, den ich je geschrieben habe, aber er ist nicht großartig. Es ist auch nicht so flexibel, da es darauf angewiesen ist, dass es eine Zuordnungsfunktion zu einem Typ mit a gibt .equals() Funktion, die die gesuchte Eigenschaft beschreibt; Ich hätte viel lieber etwas, das für Willkür funktionieren könnte Function<T, Boolean>
Weiß jemand wie?
Warum ist “wiederholen” hässlich?
–Andreas
15. August 2016 um 21:45 Uhr
Streams und Indizierung passen nicht gut zusammen. An diesem Punkt ist es normalerweise besser, auf eine Schleife im alten Stil zurückzugreifen.
– Louis Wassermann
15. August 2016 um 21:46 Uhr
@Andreas Was ich an Streams mag, ist die Trennung der sammlungsbezogenen Logik von der spezifischen Sache, die gefragt wird. In diesem Fall gibt es eine Menge verschiedener Fragen, die gestellt werden könnten, die nur vom Kern abweichen Function<T, Boolean>also sollte es eine Möglichkeit geben, damit umzugehen, die es von der allgemeinen Sammlungslogik abstrahiert.
– Eduard Peters
15. August 2016 um 21:55 Uhr
@Andreas Weil Sie dort den gesamten strukturbezogenen Code manuell beschreiben, anstatt diesen zu unterteilen. Zu der anderen Frage, das habe ich gerade vergessen Predicate war eine Sache.
– Eduard Peters
15. August 2016 um 22:09 Uhr
Saubere Java 11-Lösung: int index = users.stream().map(User::getName).takeWhile(not(username::equals)).count();
– Klitos Kyriacou
9. November 2018 um 17:11 Uhr
vsminkow
Gelegentlich gibt es keine Pythonik zipWithIndex auf Java. Also ich bin auf sowas gestoßen:
Alternativ können Sie verwenden zipWithIndex aus Protonenpaket Bibliothek
Notiz
Diese Lösung kann zeitaufwändig sein, wenn users.get kein Dauerbetrieb ist.
Das funktioniert. Ich könnte das Paar auch überspringen, indem ich einfach den Benutzer mit dem angegebenen Index in der Filteroperation nachschlage … nicht sicher, ob das besser oder schlechter ist.
– Eduard Peters
15. August 2016 um 21:38 Uhr
@EdwardPeters Es ist definitiv besser. Zumindest wird dadurch zusätzlicher Speicherverkehr eliminiert
– Wsminkow
15. August 2016 um 21:39 Uhr
@vsminkov, Sie sollten hier vorsichtig sein. Niemand hat gesagt, dass die Liste O(1)-Zugriff hat.
– Service
15. August 2016 um 21:46 Uhr
@SerCe fair genug. Die andere Möglichkeit besteht darin, einen benutzerdefinierten Spliterator zu implementieren, wie es Protonpack tut. Ich werde eine Notiz machen
Stimme Ihnen zu, können Sie mir bitte mit Vorschlägen helfen, diesen Anwendungsfall hier zu handhaben.
– AmanSinghal
15. August 2016 um 21:45 Uhr
Anstatt von get()verwenden orElse(-1)wie es andere Index-of-Operationen tun.
– Brian Götz
15. August 2016 um 21:53 Uhr
aber der Code wird hier abstürzen. users.get(userInd). getName() wenn der zurückgegebene Benutzer null ist. Kümmert sich die Stream-API auch um NullPointerException?
– AmanSinghal
15. August 2016 um 21:55 Uhr
@AmanSinghal IMO, Sie sollten keine Nullwerte in Listen haben, aber leere Listen oder Listen, denen ein angeforderter Name fehlt, sind sinnvoll. Wenn Sie eine NPE erhalten, weil jemand einen Nullwert in eine Liste eingetragen hat, ist das seine Schuld
– Eduard Peters
15. August 2016 um 22:20 Uhr
Verwendung der Guava-Bibliothek: int index =
Iterables.indexOf(users, u -> searchName.equals(u.getName()))
die am besten lesbare Lösung und Chancen guave – wie commons-lang – ist bereits in Ihrem POM 😉 IMHO eleganter als ein IntStream!
– Benutzer1075613
28. November 2020 um 2:42 Uhr
Du kannst es versuchen StreamEx Bibliothek erstellt von Tagir Walejew. Diese Bibliothek hat eine praktische #indexOf-Methode.
Dies ist ein einfaches Beispiel:
List<User> users = asList(new User("Vas"), new User("Innokenty"), new User("WAT"));
long index = StreamEx.of(users)
.indexOf(user -> user.name.equals("Innokenty"))
.getAsLong();
System.out.println(index);
Hibor
Eine Lösung ohne externe Bibliothek
AtomicInteger i = new AtomicInteger(); // any mutable integer wrapper
int index = users.stream()
.peek(v -> i.incrementAndGet())
.anyMatch(user -> user.getName().equals(username)) ? // your predicate
i.get() - 1 : -1;
Peek erhöht den Index i, während das Prädikat falsch ist, daher ist i, wenn das Prädikat wahr ist, um 1 größer als das übereinstimmende Prädikat => i.get() -1
Dies ist ein gefährlicher Ansatz, da Streams je nach Quelle parallel verarbeitet werden können, wobei der Index in diesem Fall vollständig deaktiviert sein kann. Sie sollten hinzufügen .sequential() Vor .peek() um dies zuverlässig zu machen.
– Pawel Weselow
15. Dezember 2021 um 22:58 Uhr
Donald Rab
Dort ist der detectIndex Methode in der Eclipse-Sammlungen Bibliothek, die eine nimmt Predicate.
int index = ListIterate.detectIndex(users, user -> username.equals(user.getName()));
Wenn Sie eine Methode haben User Klasse, die zurückkehrt boolean wenn username Streichhölzer können Sie Folgendes verwenden:
int index = ListIterate.detectIndexWith(users, User::named, username);
Hinweis: Ich bin ein Committer für Eclipse Collections
Dies ist ein gefährlicher Ansatz, da Streams je nach Quelle parallel verarbeitet werden können, wobei der Index in diesem Fall vollständig deaktiviert sein kann. Sie sollten hinzufügen .sequential() Vor .peek() um dies zuverlässig zu machen.
– Pawel Weselow
15. Dezember 2021 um 22:58 Uhr
12170900cookie-checkStream Methode, um den Index des ersten Elements zu erhalten, das mit dem booleschen Wert übereinstimmtyes
Warum ist “wiederholen” hässlich?
–Andreas
15. August 2016 um 21:45 Uhr
Streams und Indizierung passen nicht gut zusammen. An diesem Punkt ist es normalerweise besser, auf eine Schleife im alten Stil zurückzugreifen.
– Louis Wassermann
15. August 2016 um 21:46 Uhr
@Andreas Was ich an Streams mag, ist die Trennung der sammlungsbezogenen Logik von der spezifischen Sache, die gefragt wird. In diesem Fall gibt es eine Menge verschiedener Fragen, die gestellt werden könnten, die nur vom Kern abweichen
Function<T, Boolean>
also sollte es eine Möglichkeit geben, damit umzugehen, die es von der allgemeinen Sammlungslogik abstrahiert.– Eduard Peters
15. August 2016 um 21:55 Uhr
@Andreas Weil Sie dort den gesamten strukturbezogenen Code manuell beschreiben, anstatt diesen zu unterteilen. Zu der anderen Frage, das habe ich gerade vergessen
Predicate
war eine Sache.– Eduard Peters
15. August 2016 um 22:09 Uhr
Saubere Java 11-Lösung:
int index = users.stream().map(User::getName).takeWhile(not(username::equals)).count();
– Klitos Kyriacou
9. November 2018 um 17:11 Uhr