Ich versuche Java 8 Stream
s zu verwenden, um Elemente in a zu finden LinkedList
. Ich möchte jedoch garantieren, dass es nur eine Übereinstimmung mit den Filterkriterien gibt.
Nehmen Sie diesen Code:
public static void main(String[] args) {
LinkedList<User> users = new LinkedList<>();
users.add(new User(1, "User1"));
users.add(new User(2, "User2"));
users.add(new User(3, "User3"));
User match = users.stream().filter((user) -> user.getId() == 1).findAny().get();
System.out.println(match.toString());
}
static class User {
@Override
public String toString() {
return id + " - " + username;
}
int id;
String username;
public User() {
}
public User(int id, String username) {
this.id = id;
this.username = username;
}
public void setUsername(String username) {
this.username = username;
}
public void setId(int id) {
this.id = id;
}
public String getUsername() {
return username;
}
public int getId() {
return id;
}
}
Dieser Code findet eine User
basierend auf ihrer ID. Es gibt jedoch keine Garantie dafür, wie viele User
s zum Filter passen.
Ändern der Filterzeile in:
User match = users.stream().filter((user) -> user.getId() < 0).findAny().get();
Wird ein NoSuchElementException
(gut!) Werfen
Ich möchte, dass es einen Fehler gibt, wenn es mehrere Übereinstimmungen gibt. Gibt es eine Möglichkeit, dies zu tun?
java
lambda
java-8
java-stream
Ryvantage
quelle
quelle
count()
ist eine Terminaloperation, also können Sie das nicht tun. Der Stream kann danach nicht mehr verwendet werden.Stream::size
?Stream
s so viel besser zu verstehen als zuvor ...LinkedHashSet
Fall stellen Sie fest, dass Sie eine (vorausgesetzt, Sie möchten, dass die Einfügereihenfolge beibehalten wird) oder eineHashSet
ganze Zeit verwenden mussten. Wenn Ihre Sammlung nur zum Auffinden einer einzelnen Benutzer-ID verwendet wird, warum sammeln Sie dann alle anderen Elemente? Wenn es ein Potenzial gibt, dass Sie immer eine Benutzer-ID finden müssen, die ebenfalls eindeutig sein muss, warum dann eine Liste und keine Menge verwenden? Sie programmieren rückwärts. Verwenden Sie die richtige Sammlung für den Job und sparen Sie sich diese KopfschmerzenAntworten:
Erstellen Sie eine benutzerdefinierte
Collector
Wir verwenden
Collectors.collectingAndThen
, um unsere gewünschtenCollector
von zu konstruierenList
mit demCollectors.toList()
Sammler.IllegalStateException
if auslöstlist.size != 1
.Benutzt als:
Sie können dies dann beliebig anpassen
Collector
, z. B. die Ausnahme als Argument im Konstruktor angeben, sie so anpassen , dass zwei Werte zulässig sind, und mehr.Eine alternative - wohl weniger elegante - Lösung:
Sie können eine 'Problemumgehung' verwenden, die eine
peek()
und eine beinhaltetAtomicInteger
, aber das sollten Sie wirklich nicht verwenden.Was Sie tun können, ist, es einfach in einem zu sammeln
List
, wie folgt :quelle
Iterables.getOnlyElement
würden diese Lösungen verkürzen und bessere Fehlermeldungen liefern. Nur als Tipp für andere Leser, die bereits Google Guava verwenden.singletonCollector()
Definition der Version verworfen wird, dass Reste in der Post, und Umbenennung zutoSingleton()
. Meine Java-Stream-Kenntnisse sind etwas verrostet, aber das Umbenennen scheint mir hilfreich zu sein. Das Überprüfen dieser Änderung hat 2 Minuten gedauert. Wenn Sie keine Zeit haben, Änderungen zu überprüfen, kann ich vorschlagen, dass Sie in Zukunft jemanden bitten, dies zu tun, möglicherweise im Java-Chatroom ?Der Vollständigkeit halber ist hier der 'Einzeiler', der der hervorragenden Antwort von @ prunge entspricht:
Dies erhält das einzige passende Element aus dem Strom, das wirft
NoSuchElementException
falls der Stream leer ist, oderIllegalStateException
falls der Stream mehr als ein übereinstimmendes Element enthält.Eine Variation dieses Ansatzes vermeidet das frühzeitige Auslösen einer Ausnahme und stellt das Ergebnis stattdessen so dar
Optional
, dass es entweder das einzige Element oder nichts (leer) enthält, wenn null oder mehrere Elemente vorhanden sind:quelle
get()
inorElseThrow()
Die anderen Antworten, die das Schreiben eines Brauchs beinhalten,
Collector
sind wahrscheinlich effizienter (wie die von Louis Wasserman , +1), aber wenn Sie Kürze wünschen, würde ich Folgendes vorschlagen:Überprüfen Sie dann die Größe der Ergebnisliste.
quelle
limit(2)
diese Lösung? Welchen Unterschied würde es machen, ob die resultierende Liste 2 oder 100 war? Wenn es größer als 1.Collectors.collectingAndThen(toList(), l -> { if (l.size() == 1) return l.get(0); throw new RuntimeException(); })
maxSize: the number of elements the stream should be limited to
. Also sollte es nicht.limit(1)
statt sein.limit(2)
?result.size()
, ob er gleich 1 ist. Wenn es 2 ist, gibt es mehr als eine Übereinstimmung, also ist es ein Fehler. Wenn der Code dies stattdessen tun würdelimit(1)
, würde mehr als eine Übereinstimmung zu einem einzelnen Element führen, das nicht von genau einer Übereinstimmung unterschieden werden kann. Dies würde einen Fehlerfall übersehen, über den das OP besorgt war.Guave bietet,
MoreCollectors.onlyElement()
was hier das Richtige tut. Aber wenn Sie es selbst tun müssen, können Sie Ihre eigenenCollector
dafür rollen :... oder verwenden Sie
Holder
stattdessen Ihren eigenen TypAtomicReference
. Sie können dasCollector
so oft wiederverwenden, wie Sie möchten.quelle
Collector
war der richtige Weg.List
teurer ist als eine einzelne veränderbare Referenz.MoreCollectors.onlyElement()
sollte eigentlich der erste (und vielleicht der einzige :) seinVerwenden Sie Guavas
MoreCollectors.onlyElement()
( JavaDoc ).Es macht, was Sie wollen und wirft ein,
IllegalArgumentException
wenn der Stream aus zwei oder mehr Elementen besteht, und ein,NoSuchElementException
wenn der Stream leer ist.Verwendung:
quelle
MoreCollectors
ist Teil der noch nicht veröffentlichten (Stand 2016-12) unveröffentlichten Version 21.Die "Escape Hatch" -Operation, mit der Sie seltsame Dinge tun können, die sonst nicht von Streams unterstützt werden, besteht darin, nach einem zu fragen
Iterator
:Guave hat eine bequeme Methode, um ein
Iterator
und das einzige Element zu erhalten, das geworfen wird, wenn es null oder mehrere Elemente gibt, die die unteren n-1-Zeilen hier ersetzen könnten.quelle
Aktualisieren
Netter Vorschlag im Kommentar von @Holger:
Ursprüngliche Antwort
Die Ausnahme wird von ausgelöst
Optional#get
, aber wenn Sie mehr als ein Element haben, hilft das nicht. Sie können die Benutzer in einer Sammlung sammeln, die nur ein Element akzeptiert, z. B.:das wirft ein
java.lang.IllegalStateException: Queue full
, aber das fühlt sich zu hacky an.Oder Sie können eine Ermäßigung in Kombination mit einer optionalen Option verwenden:
Die Reduzierung ergibt im Wesentlichen:
Das Ergebnis wird dann in eine Option eingeschlossen.
Aber die einfachste Lösung wäre wahrscheinlich, einfach in einer Sammlung zu sammeln, zu überprüfen, ob ihre Größe 1 ist, und das einzige Element zu erhalten.
quelle
null
) hinzufügen , um die Verwendung zu verhindernget()
. Leiderreduce
funktioniert Ihr nicht so, wie Sie denken, denken Sie daran,Stream
dass esnull
Elemente enthält, vielleicht denken Sie, dass Sie es abgedeckt haben, aber ich kann es sein[User#1, null, User#2, null, User#3]
, jetzt wird es keine Ausnahme werfen, denke ich, es sei denn, ich irre mich hier.null
an die Reduktionsfunktion übergeben werden kann, würde das Entfernen des Identitätswertarguments den gesamten Umgang mitnull
der Funktion überflüssig machen: Erledigt denreduce( (u,v) -> { throw new IllegalStateException("More than one ID found"); } )
Job und noch besser, er gibt bereits eine zurückOptional
, sodass die Notwendigkeit des AufrufsOptional.ofNullable
der nicht mehr besteht Ergebnis.Eine Alternative ist die Verwendung der Reduzierung: (In diesem Beispiel werden Zeichenfolgen verwendet, sie können jedoch problemlos auf alle Objekttypen angewendet werden, einschließlich
User
)Also für den Fall mit
User
Ihnen hätte:quelle
Verwenden Sie reduzieren
Dies ist der einfachere und flexiblere Weg, den ich gefunden habe (basierend auf der Antwort von @prunge).
Auf diese Weise erhalten Sie:
Optional.empty()
wenn nicht vorhandenquelle
Ich denke, dieser Weg ist einfacher:
quelle
Verwenden eines
Collector
:Verwendung:
Wir geben ein zurück
Optional
, da wir normalerweise nicht davon ausgehen können, dass dasCollection
genau ein Element enthält. Wenn Sie bereits wissen, dass dies der Fall ist, rufen Sie an:Dies belastet den Anrufer mit der Behandlung des Fehlers - wie es sollte.
quelle
Guave hat dafür einen
Collector
NamenMoreCollectors.onlyElement()
.quelle
Wir können RxJava (sehr leistungsfähige reaktive Erweiterungsbibliothek ) verwenden.
Der einzelne Operator löst eine Ausnahme aus, wenn kein Benutzer oder mehr als ein Benutzer gefunden wird.
quelle
Da
Collectors.toMap(keyMapper, valueMapper)
eine Wurffusion verwendet wird, um mehrere Einträge mit demselben Schlüssel zu verarbeiten, ist es einfach:Sie erhalten einen
IllegalStateException
für doppelte Schlüssel. Aber am Ende bin ich mir nicht sicher, ob der Code mit einem nicht noch besser lesbar wäreif
.quelle
.collect(Collectors.toMap(user -> "", Function.identity())).get("")
, haben Sie ein allgemeineres Verhalten.Ich benutze diese beiden Sammler:
quelle
onlyOne()
wirftIllegalStateException
für> 1 Elemente und NoSuchElementException` (inOptional::get
) für 0 Elemente.Supplier
von zu nehmen(Runtime)Exception
.Wenn es Ihnen nichts ausmacht, eine Bibliothek eines Drittanbieters zu verwenden, haben beide
SequenceM
aus Cyclops-Streams (undLazyFutureStream
aus Simple- React-Operatoren) einen und einen einzigen Operator.singleOptional()
löst eine Ausnahme aus, wenn das Element mindestens0
mehrere1
Elemente enthältStream
, andernfalls wird der einzelne Wert zurückgegeben.singleOptional()
Gibt zurück,Optional.empty()
wenn der Wert keine oder mehr als einen Wert enthältStream
.Offenlegung - Ich bin der Autor beider Bibliotheken.
quelle
Ich habe mich für den direkten Ansatz entschieden und das Ding einfach umgesetzt:
mit dem JUnit-Test:
Diese Implementierung ist nicht threadsicher.
quelle
quelle
Hast du das versucht?
Quelle: https://docs.oracle.com/javase/8/docs/api/java/util/stream/Stream.html
quelle
count()
die Verwendung nicht gut ist, da es sich um eine Terminaloperation handelt.