Logotipo de Zephyrnet

Java: filtre una secuencia con expresiones lambda

Fecha:

Java Streams se introdujo en Java 8 en 2014, en un esfuerzo por introducir Java detallado en un paradigma de programación funcional. Java Streams expone muchas operaciones funcionales flexibles y potentes para realizar el procesamiento de colecciones en frases ingeniosas.

Filtrar colecciones en función de algún predicado sigue siendo una de las operaciones funcionales más utilizadas y se puede realizar con un Predicate o más concisamente – con un Expresión Lambda.

En esta breve guía, veremos cómo puede filtrar un flujo de Java 8 con expresiones Lambda.

Filtrado de flujos en Java

En general, cualquier Stream se puede filtrar a través de la filter() método, y un predicado dado:

Stream filter(Predicate<? super T> predicate)

Cada elemento de la secuencia se ejecuta contra el predicado y se agrega a la secuencia de salida si el predicado devuelve true. Puede suministrar un Predicate ejemplo:

Predicate contains = s -> s.contains("_deprecated");
List results = stream.filter(contains).collect(Collectors.toList());

O simplifique proporcionando una expresión Lambda:

List results = stream.filter(s -> s.contains("_deprecated"))
                             .collect(Collectors.toList());

O incluso colapsar la Expresión Lambda en una referencia de método:


List results = stream.filter(String::isEmpty)
                             .collect(Collectors.toList());

Sin embargo, con las referencias a métodos, no puede pasar argumentos, puede definir métodos en el objeto que está filtrando y adaptarlos para que se puedan filtrar fácilmente (siempre que el método no acepte argumentos y devuelva un boolean).

Recuerda que los flujos no son colecciones - son arroyos de colecciones, y tendrá que volver a recopilarlos en cualquier colección, como una List, Map, etc. para darles permanencia. Además, todas las operaciones realizadas en elementos de flujo ya sea intermedio or terminal:

  • Las operaciones intermedias devuelven una nueva secuencia con cambios de la operación anterior
  • Las operaciones de terminal devuelven un tipo de datos y están destinadas a finalizar una canalización de procesamiento en una secuencia

filter() es un intermedio operación, y está destinado a ser encadenado con otras operaciones intermedias, antes de que finalice la secuencia. Para persistir cualquier cambio (como cambios en los propios elementos o resultados filtrados), deberá asignar el resultado flujo de salida a una nueva variable de referencia, a través de una operación de terminal.

Nota: Incluso al encadenar muchas expresiones lambda, es posible que no tenga problemas de legibilidad con los saltos de línea adecuados.

En los siguientes ejemplos, trabajaremos con esta lista de libros:

Book book1 = new Book("001", "Our Mathematical Universe", "Max Tegmark", 432, 2014);
Book book2 = new Book("002", "Life 3.0", "Max Tegmark", 280, 2017);
Book book3 = new Book("003", "Sapiens", "Yuval Noah Harari", 443, 2011);
        
List books = Arrays.asList(book1, book2, book3);

Filtrar colección con Stream.filter()

Filtremos esta colección de libros. Cualquier predicado vale, así que, por ejemplo, filtremos por qué libros tienen más de 400 páginas:

List results = books.stream()
                          .filter(b -> b.getPageNumber() > 400)
                          .collect(Collectors.toList());

Esto da como resultado una lista que contiene:

[
Book{id='001', name='Our Mathematical Universe', author='Max Tegmark', pageNumber=432, publishedYear=2014}, 
Book{id='003', name='Sapiens', author='Yuval Noah Harari', pageNumber=443, publishedYear=2011}
]

A la hora de filtrar, un método realmente útil para encadenar es map(), que le permite asignar objetos a otro valor. Por ejemplo, podemos asignar cada libro a su nombre y, por lo tanto, devolver solo el nombres de los libros que se ajustan al predicado del filter() llamada:

List results = books.stream()
                            .filter(b -> b.getPageNumber() > 400)
                            .map(Book::getName)
                            .collect(Collectors.toList());

Esto da como resultado una lista de cadenas:

[Our Mathematical Universe, Sapiens]

Filtrar colección en múltiples predicados con Stream.filter()

Por lo general, nos gustaría filtrar las colecciones por más de un criterio. Esto se puede hacer encadenando múltiples filter() llamadas or utilizando un predicado de cortocircuito, que verifica dos condiciones en una sola filter() llamada.

 List results = books.stream()
                    .filter(b -> b.getPageNumber() > 400 && b.getName().length() > 10)
                    .collect(Collectors.toList());
                    


 List results2 = books.stream()
                    .filter(b -> b.getPageNumber() > 400)
                    .filter(b -> b.getName().length() > 10)
                    .collect(Collectors.toList());

Consulte nuestra guía práctica y práctica para aprender Git, con las mejores prácticas, los estándares aceptados por la industria y la hoja de trucos incluida. Deja de buscar en Google los comandos de Git y, de hecho, aprenden ella!

Cuando se utilizan múltiples criterios, las llamadas lambda pueden ser algo largas. En este punto, extraerlos como predicados independientes podría ofrecer más claridad. Sin embargo, ¿qué enfoque es más rápido?

¿Filtro único con condición compleja o filtros múltiples?

Depende de su hardware, qué tan grande es su colección y si usa flujos paralelos o no. En general, un filtro con una condición compleja superará a múltiples filtros con condiciones más simples (colecciones pequeñas a medianas) o funcionará al mismo nivel (colecciones muy grandes). Si sus condiciones son demasiado largas, puede beneficiarse al distribuirlas en múltiples filter() pide, por la mejora de la legibilidad, ya que el rendimiento es muy similar.

La mejor opción es probar ambos, tenga en cuenta el rendimiento en el dispositivo de destinoy ajuste su estrategia en consecuencia.

usuario de GitHub Volkodavs hizo un punto de referencia de filtrado en operaciones/s de rendimiento y alojó los resultados en el "javafilters-benchmarks" repositorio. Los resultados se resumen en una tabla informativa:

Muestra una clara disminución de los rendimientos en tamaños de colección más grandes, con ambos enfoques funcionando en torno al mismo nivel. Los flujos paralelos se benefician significativamente en tamaños de colección más grandes, pero frenan el rendimiento en tamaños más pequeños (por debajo de ~10k elementos). Vale la pena señalar que los flujos paralelos mantuvieron su rendimiento mucho mejor que los flujos no paralelos, lo que los hace significativamente más robustos para la entrada.

punto_img

Información más reciente

punto_img

vidacienciav

café vc

café vc

vidacienciav