API de Java 8 Streams peek ()

1. Introducción

La API Java Stream nos presenta una poderosa alternativa para procesar datos.

En este breve tutorial, nos centraremos en peek () , un método a menudo mal entendido.

2. Ejemplo rápido

Ensuciemos nuestras manos e intentemos usar peek () . Tenemos una serie de nombres y queremos imprimirlos en la consola.

Dado que peek () espera un consumidor como único argumento, parece una buena opción, así que intentémoslo:

Stream nameStream = Stream.of("Alice", "Bob", "Chuck"); nameStream.peek(System.out::println);

Sin embargo, el fragmento de código anterior no produce ningún resultado. Para entender por qué, hagamos un repaso rápido sobre aspectos del ciclo de vida de la transmisión.

3. Operaciones intermedias frente a terminales

Recuerde que los flujos tienen tres partes: una fuente de datos, cero o más operaciones intermedias y cero o una operación de terminal.

La fuente proporciona los elementos a la canalización.

Las operaciones intermedias obtienen elementos uno por uno y los procesan. Todas las operaciones intermedias son perezosas y, como resultado, ninguna operación tendrá ningún efecto hasta que la tubería comience a funcionar.

Las operaciones de terminal significan el final del ciclo de vida de la transmisión. Lo más importante para nuestro escenario es que inician el trabajo en proceso .

4. Uso de peek ()

La razón por la que peek () no funcionó en nuestro primer ejemplo es que es una operación intermedia y no aplicamos una operación de terminal a la tubería. Alternativamente, podríamos haber usado forEach () con el mismo argumento para obtener el comportamiento deseado:

Stream nameStream = Stream.of("Alice", "Bob", "Chuck"); nameStream.forEach(System.out::println);

La página Javadoc de peek () dice: " Este método existe principalmente para admitir la depuración, donde desea ver los elementos a medida que fluyen más allá de un cierto punto en una tubería ".

Consideremos este fragmento de la misma página de Javadoc:

Stream.of("one", "two", "three", "four") .filter(e -> e.length() > 3) .peek(e -> System.out.println("Filtered value: " + e)) .map(String::toUpperCase) .peek(e -> System.out.println("Mapped value: " + e)) .collect(Collectors.toList());

Demuestra, cómo observamos los elementos que pasaron cada operación.

Además de eso, peek () puede ser útil en otro escenario: cuando queremos alterar el estado interno de un elemento . Por ejemplo, digamos que queremos convertir todos los nombres de usuario a minúsculas antes de imprimirlos:

Stream userStream = Stream.of(new User("Alice"), new User("Bob"), new User("Chuck")); userStream.peek(u -> u.setName(u.getName().toLowerCase())) .forEach(System.out::println);

Alternativamente, podríamos haber usado map () , pero peek () es más conveniente ya que no queremos reemplazar el elemento.

5. Conclusión

En este breve tutorial, vimos un resumen del ciclo de vida de la transmisión para comprender cómo funciona peek () . También vimos dos casos de uso diario en los que usar peek () es la opción más sencilla.

Y como de costumbre, los ejemplos están disponibles en GitHub.