Angenommen, ich habe einen Datenstrom von Dingen und möchte diese in der Mitte des Datenstroms "anreichern". peek()
Dazu kann ich Folgendes verwenden :
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Nehmen Sie an, dass das Mutieren der Dinge an dieser Stelle im Code korrekt ist - zum Beispiel das thingMutator
Methode das Feld "lastProcessed" möglicherweise auf die aktuelle Zeit.
Jedoch, peek()
meisten Kontexten bedeutet es jedoch "schauen, aber nicht anfassen".
Ist die Verwendung peek()
zum Mutieren von Stream-Elementen ein Gegenmuster oder eine schlechte Empfehlung?
Bearbeiten:
Der alternative, konventionellere Ansatz wäre die Umstellung des Verbrauchers:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
zu einer Funktion, die den Parameter zurückgibt:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
und benutze map()
stattdessen:
stream.map(this::thingMutator)...
Aber das einleitet perfunctory Code ( return
) und ich bin nicht davon überzeugt , es ist klarer, weil Sie wissen , peek()
kehren das gleiche Objekt, aber mit map()
ihm nicht einmal auf einem Blick klar ist , dass es die gleiche Klasse des Objekts.
Weiter kann mit peek()
dir ein Lambda sein, das mutiert, aber mit map()
dir musst du ein Zugunglück bauen. Vergleichen Sie:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Ich denke der peek()
Version ist klarer und das Lambda mutiert eindeutig, so dass es keine "mysteriösen" Nebenwirkungen gibt. In ähnlicher Weise ist auch dies klar und offensichtlich, wenn eine Methodenreferenz verwendet wird und der Name der Methode eindeutig eine Mutation impliziert.
Persönlich scheue ich mich nicht davor, peek()
zu mutieren - ich finde es sehr praktisch.
quelle
peek
mit einem Stream gearbeitet, der seine Elemente dynamisch generiert? Funktioniert es noch oder gehen die Änderungen verloren? Das Ändern der Elemente eines Streams klingt für mich unzuverlässig.List<Thing> list; things.stream().peek(list::add).forEach(...);
sehr praktisch. In letzter Zeit. Ich habe es verwendet Infos hinzufügen für die Veröffentlichung:Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());
. Ich weiß, dass es andere Möglichkeiten gibt, dieses Beispiel umzusetzen, aber ich vereinfache es hier stark. Mitpeek()
kompakter und eleganter Code IMHO. Abgesehen von der Lesbarkeit handelt es sich bei dieser Frage wirklich um das, was Sie angesprochen haben. Ist es sicher / zuverlässig?peek
? Ich habe eine ähnliche Frage zum Stackoverflow und hoffe, Sie könnten es sich ansehen und Ihr Feedback geben. Vielen Dank. stackoverflow.com/questions/47356992/…Antworten:
Sie haben Recht, "spähen" im englischen Sinne bedeutet "gucken, aber nicht anfassen".
Doch die JavaDoc heißt es :
Schlüsselwörter: "Performing ... Action" und "Consumed". Das JavaDoc ist sehr klar, dass wir erwarten sollten
peek
, die Möglichkeit zu haben, den Stream zu modifizieren.In JavaDoc heißt es jedoch auch:
Dies weist darauf hin, dass es eher zur Beobachtung gedacht ist, z. B. zum Protokollieren von Elementen im Stream.
Ich gehe davon aus, dass wir Aktionen mit den Elementen im Stream ausführen können , aber vermeiden sollten, Elemente im Stream zu mutieren . Rufen Sie beispielsweise Methoden für die Objekte auf, vermeiden Sie jedoch Mutationen.
Zumindest möchte ich Ihrem Code einen kurzen Kommentar hinzufügen:
In Bezug auf die Nützlichkeit solcher Kommentare gehen die Meinungen auseinander, aber ich würde in diesem Fall einen solchen Kommentar verwenden.
quelle
thingMutator
oder konkreterresetLastProcessed
usw. Sofern kein zwingender Grund vorliegt, weisen die erforderlichen Kommentare wie Ihr Vorschlag in der Regel auf eine schlechte Auswahl von Variablen- und / oder Methodennamen hin. Wenn gute Namen gewählt werden, ist das nicht genug? Oder sagen Sie, dass trotz eines guten Namens alles inpeek()
wie ein blinder Fleck ist, den die meisten Programmierer "überfliegen" (visuell überspringen) würden? Außerdem ist "hauptsächlich zum Debuggen" nicht dasselbe wie "nur zum Debuggen" - welche anderen Verwendungen als "hauptsächlich" waren vorgesehen?Es könnte leicht falsch interpretiert werden, daher würde ich es vermeiden, es so zu verwenden. Die wahrscheinlich beste Option ist die Verwendung einer Lambda-Funktion zum Zusammenführen der beiden erforderlichen Aktionen in den forEach-Aufruf. Möglicherweise möchten Sie auch ein neues Objekt zurückgeben, anstatt das vorhandene zu mutieren. Es ist möglicherweise etwas weniger effizient, führt jedoch wahrscheinlich zu besser lesbarem Code und verringert das Risiko, dass die geänderte Liste versehentlich für einen anderen Vorgang verwendet wird, der ausgeführt werden sollte habe das Original erhalten.
quelle
Der API-Hinweis sagt uns, dass die Methode hauptsächlich für Aktionen wie Debuggen / Protokollieren / Auslösen von Statistiken usw. hinzugefügt wurde.
@apiNote Diese Methode dient hauptsächlich zur Unterstützung des Debuggens, bei dem * die Elemente angezeigt werden sollen, wenn sie an einem bestimmten Punkt in einer Pipeline vorbeifließen: *
quelle