Supongamos que tengo una secuencia de Cosas y quiero "enriquecerlas" a mitad de la secuencia, puedo usar peek()
para hacer esto, por ejemplo:
streamOfThings.peek(this::thingMutator).forEach(this::someConsumer);
Suponga que mutar las cosas en este punto del código es un comportamiento correcto; por ejemplo, el thingMutator
método puede establecer el campo "lastProcessed" en la hora actual.
Sin embargo, peek()
en la mayoría de los contextos significa "mirar, pero no tocar".
¿Está utilizando peek()
para mutar elementos de la corriente un antipatrón o mal aconsejado?
Editar:
El enfoque alternativo, más convencional, sería convertir al consumidor:
private void thingMutator(Thing thing) {
thing.setLastProcessed(System.currentTimeMillis());
}
a una función que devuelve el parámetro:
private Thing thingMutator(Thing thing) {
thing.setLastProcessed(currentTimeMillis());
return thing;
}
y usar map()
en su lugar:
stream.map(this::thingMutator)...
Pero eso introduce código superficial (the return
) y no estoy convencido de que sea más claro, porque sabes que peek()
devuelve el mismo objeto, pero con map()
ni siquiera es claro de un vistazo que es la misma clase de objeto.
Además, con peek()
usted puede tener una lambda que muta, pero con map()
usted tiene que construir un choque de trenes. Comparar:
stream.peek(t -> t.setLastProcessed(currentTimeMillis())).forEach(...)
stream.map(t -> {t.setLastProcessed(currentTimeMillis()); return t;}).forEach(...)
Creo que la peek()
versión es más clara y la lambda está claramente mutando, por lo que no hay ningún efecto secundario "misterioso". Del mismo modo, si se usa una referencia de método y el nombre del método implica claramente una mutación, eso también es claro y obvio.
En una nota personal, no evito usar peek()
para mutar, me parece muy conveniente.
fuente
peek
una secuencia que genera sus elementos dinámicamente? ¿Sigue funcionando o se pierden los cambios? Modificar los elementos de una transmisión no me parece confiable.List<Thing> list; things.stream().peek(list::add).forEach(...);
muy útil. Últimamente. Lo he utilizado para agregar información de la publicación:Map<Thing, Long> timestamps = ...; return things.stream().peek(t -> t.setTimestamp(timestamp.get(t))).collect(toList());
. Sé que hay otras formas de hacer este ejemplo, pero estoy simplificando demasiado aquí. El usopeek()
produce un código más compacto y elegante en mi humilde opinión. Dejando a un lado la legibilidad, esta pregunta es realmente sobre lo que ha planteado; ¿es seguro / confiable?peek
? Tengo una pregunta similar sobre stackoverflow y espero que puedas verla y dar tu opinión. Gracias. stackoverflow.com/questions/47356992/…Respuestas:
Tienes razón, "mirar" en el sentido inglés de la palabra significa "mirar, pero no tocar".
Sin embargo los JavaDoc estados:
Palabras clave: "realizando ... acción" y "consumido". El JavaDoc tiene muy claro que debemos esperar
peek
tener la capacidad de modificar la secuencia.Sin embargo, JavaDoc también establece:
Esto indica que está destinado más a la observación, por ejemplo, elementos de registro en la secuencia.
Lo que deduzco de todo esto es que podemos realizar acciones utilizando los elementos en la secuencia, pero debemos evitar la mutación de elementos en la secuencia. Por ejemplo, siga adelante y llame a métodos en los objetos, pero trate de evitar las operaciones de mutación en ellos.
Como mínimo, agregaría un breve comentario a su código a lo largo de estas líneas:
Las opiniones difieren sobre la utilidad de tales comentarios, pero usaría ese comentario en este caso.
fuente
thingMutator
, o más concreto,resetLastProcessed
etc. A menos que haya una razón convincente, los comentarios de necesidad como su sugerencia generalmente indican malas elecciones de nombres de variables y / o métodos. Si se eligen buenos nombres, ¿no es eso suficiente? ¿O estás diciendo que a pesar de un buen nombre, algo en el interiorpeek()
es como un punto ciego que la mayoría de los programadores "echarían un vistazo"? Además, "principalmente para la depuración" no es lo mismo que "solo para la depuración": ¿qué usos distintos de los "principalmente" estaban destinados?Podría malinterpretarse fácilmente, por lo que evitaría usarlo así. Probablemente la mejor opción es utilizar una función lambda para fusionar las dos acciones que necesita en la llamada forEach. También puede considerar devolver un nuevo objeto en lugar de mutar el existente; puede ser un poco menos eficiente, pero es probable que genere un código más legible y reduzca el potencial de usar accidentalmente la lista modificada para otra operación que debería Han recibido el original.
fuente
La nota de API nos dice que el método se ha agregado principalmente para acciones como depuración / registro / estadísticas de disparo, etc.
@apiNote Este método existe principalmente para admitir la depuración, donde desea * ver los elementos a medida que fluyen más allá de cierto punto en una tubería: *
fuente