Supposons que j'ai un flux de choses et que je veuille les "enrichir" à mi-parcours, je peux utiliser peek()
ceci, par exemple:
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Supposons que la mutation des objets à ce stade du code constitue un comportement correct. Par exemple, la thingMutator
méthode peut définir le champ "lastProcessed" à l'heure actuelle.
Cependant, peek()
dans la plupart des contextes, cela signifie "regarde, mais ne touche pas".
L'utilisation peek()
de la mutation d' éléments de flux est-elle un anti-modèle ou une mauvaise idée?
Modifier:
L’approche alternative, plus conventionnelle, consisterait à convertir le consommateur:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
à une fonction qui retourne le paramètre:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
et utilisez map()
plutôt:
stream.map(this::thingMutator)...
Mais cela introduit un code superficiel (le return
) et je ne suis pas convaincu que ce soit plus clair, car vous savez qu'il peek()
retourne le même objet, mais map()
il n'est même pas évident en un coup d'œil qu'il s'agisse de la même classe d'objet.
De plus, avec peek()
vous , vous pouvez avoir un lambda qui mute, mais avec map()
vous, vous devez construire une épave de train. Comparer:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Je pense que la peek()
version est plus claire et que le lambda est en train de muter, il n’ya donc pas d’effet secondaire "mystérieux". De même, si une référence de méthode est utilisée et que le nom de la méthode implique clairement une mutation, cela aussi est clair et évident.
Sur une note personnelle, je n'hésite pas à utiliser peek()
pour muter - je trouve cela très pratique.
la source
peek
avec un flux qui génère ses éléments de manière dynamique? Cela fonctionne-t-il toujours ou les modifications sont-elles perdues? La modification des éléments d'un flux ne me semble pas fiable.List<Thing> list; things.stream().peek(list::add).forEach(...);
très pratique. Dernièrement. Je l' ai utilisé pour ajouter des informations pour la publication:Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());
. Je sais qu'il existe d'autres façons de faire cet exemple, mais je simplifie énormément ici. Utilisationpeek()
produit un code IMHO plus compact et plus élégant. La lisibilité mise à part, cette question concerne vraiment ce que vous avez soulevé; est-ce sûr / fiable?peek
? J'ai une question similaire sur stackoverflow et j'espère que vous pourrez l'examiner et donner votre avis. Merci. stackoverflow.com/questions/47356992/…Réponses:
Vous avez raison, "peek" au sens anglais du mot signifie "regardez, mais ne touchez pas".
Cependant, le JavaDoc indique:
Mots clés: "effectuer ... une action" et "consommé". Le JavaDoc est très clair que nous devrions nous attendre
peek
à avoir la possibilité de modifier le flux.Cependant, le JavaDoc indique également:
Cela indique qu'il est davantage destiné à l'observation, par exemple en enregistrant des éléments dans le flux.
Ce que je retiens de tout cela, c'est que nous pouvons effectuer des actions en utilisant les éléments du flux, tout en évitant de les transformer en mutation . Par exemple, continuez et appelez les méthodes sur les objets, mais essayez d'éviter de muter les opérations sur ceux-ci.
À tout le moins, je voudrais ajouter un bref commentaire à votre code dans ce sens:
Les avis diffèrent quant à l'utilité de tels commentaires, mais je les utiliserais dans ce cas.
la source
thingMutator
, ou plus concrète,resetLastProcessed
etc. À moins d'une raison impérieuse, les commentaires de nécessité tels que votre suggestion indiquent généralement un choix médiocre de noms de variables et / ou de méthodes. Si de bons noms sont choisis, n'est-ce pas suffisant? Ou êtes-vous en train de dire que malgré une bonne réputation, tout ce qui se passepeek()
est comme un angle mort que la plupart des programmeurs «écrémeraient» (sauteraient visuellement)? En outre, "principalement pour le débogage" n'est pas la même chose que "uniquement pour le débogage" - à quels usages autres que "principalement"?Cela pourrait être facilement mal interprété, aussi je voudrais éviter de l'utiliser comme ceci. La meilleure option consiste probablement à utiliser une fonction lambda pour fusionner les deux actions nécessaires dans l'appel forEach. Vous pouvez également envisager de renvoyer un nouvel objet plutôt que de le transformer: il est peut-être légèrement moins efficace, mais il est probable que le code sera plus lisible et réduira le risque d'utilisation accidentelle de la liste modifiée pour une autre opération ont reçu l'original.
la source
La note de l'API nous indique que la méthode a été ajoutée principalement pour des actions telles que les statistiques de débogage / journalisation / activation, etc.
@apiNote Cette méthode existe principalement pour prendre en charge le débogage, où vous souhaitez * voir les éléments lorsqu'ils dépassent un certain point dans un pipeline: *
la source