1. Información general
En este breve tutorial, veremos diferentes formas de filtrar una colección en Java , es decir, encontrar todos los elementos que cumplen una determinada condición.
Esta es una tarea fundamental que está presente en prácticamente cualquier aplicación Java.
Por esta razón, la cantidad de bibliotecas que brindan funcionalidad para este propósito es significativa.
En particular, en este tutorial cubriremos:
- Función filter () de Java 8 Streams
- Java 9 filtrado colector
- API de colecciones de Eclipse relevantes
- Método de filtro () CollectionUtils de Apache
- Enfoque de filtro () de Colecciones2 de Guava
2. Uso de Streams
Desde que se introdujo Java 8, Streams ha ganado un papel clave en la mayoría de los casos en los que tenemos que procesar una colección de datos.
En consecuencia, este es el enfoque preferido en la mayoría de los casos, ya que está construido en Java y no requiere dependencias adicionales.
2.1. Filtrar una colección con secuencias
En aras de la simplicidad, en todos los ejemplos nuestro objetivo será crear un método que recupere solo los números pares de una colección de valores enteros .
Por lo tanto, podemos expresar la condición que usaremos para evaluar cada elemento como ' valor% 2 == 0 '.
En todos los casos, tendremos que definir esta condición como un objeto Predicado :
public Collection findEvenNumbers(Collection baseCollection) { Predicate streamsPredicate = item -> item % 2 == 0; return baseCollection.stream() .filter(streamsPredicate) .collect(Collectors.toList()); }
Es importante tener en cuenta que cada biblioteca que analizamos en este tutorial proporciona su propia implementación de Predicate , pero aún así, todas están definidas como interfaces funcionales, lo que nos permite usar funciones Lambda para declararlas.
En este caso, usamos un recopilador predefinido proporcionado por Java que acumula los elementos en una lista , pero podríamos haber usado otros, como se discutió en esta publicación anterior.
2.2. Filtrado después de agrupar una colección en Java 9
Las secuencias nos permiten agregar elementos utilizando el recopilador groupingBy .
Sin embargo, si filtramos como hicimos en la última sección, algunos elementos podrían descartarse en una etapa temprana, antes de que este coleccionista entre en juego.
Por esta razón, el filtrado de colector se introdujo con Java 9, con el objetivo de procesar los subgrupos después de que han sido agrupados.
Siguiendo nuestro ejemplo, imaginemos que queremos agrupar nuestra colección en función del número de dígitos que tiene cada entero, antes de filtrar los números impares:
public Map
findEvenNumbersAfterGrouping( Collection baseCollection) { Function getQuantityOfDigits = item -> (int) Math.log10(item) + 1; return baseCollection.stream() .collect(groupingBy( getQuantityOfDigits, filtering(item -> item % 2 == 0, toList()))); }
En resumen, si usamos este recopilador, podríamos terminar con una entrada de valor vacía, mientras que si filtramos antes de agrupar, el recopilador no crearía dicha entrada en absoluto.
Por supuesto, elegiríamos el enfoque en función de nuestros requisitos.
3. Uso de colecciones de Eclipse
También podemos hacer uso de otras bibliotecas de terceros para lograr nuestro objetivo, ya sea porque nuestra aplicación no es compatible con Java 8 o porque queremos aprovechar alguna funcionalidad poderosa que no proporciona Java.
Tal es el caso de Eclipse Collections , una biblioteca que se esfuerza por mantenerse al día con los nuevos paradigmas, evolucionando y adoptando los cambios introducidos por todas las últimas versiones de Java.
Podemos comenzar explorando nuestra publicación Introductoria de colecciones de Eclipse para tener un conocimiento más amplio de la funcionalidad proporcionada por esta biblioteca.
3.1. Dependencias
Comencemos agregando la siguiente dependencia al pom.xml de nuestro proyecto :
org.eclipse.collections eclipse-collections 9.2.0
Las colecciones de eclipse incluyen todas las interfaces de estructura de datos necesarias y la propia API.
3.2. Filtrado de una colección con colecciones Eclipse
Usemos ahora la funcionalidad de filtrado de eclipse en una de sus estructuras de datos, como su MutableList :
public Collection findEvenNumbers(Collection baseCollection) { Predicate eclipsePredicate = item -> item % 2 == 0; Collection filteredList = Lists.mutable .ofAll(baseCollection) .select(eclipsePredicate); return filteredList; }
Como alternativa, podríamos haber utilizado el Iterar s' select ()método estático para definir el objeto FilterList :
Collection filteredList = Iterate.select(baseCollection, eclipsePredicate);
4. Uso de CollectionUtils de Apache
Para comenzar con la biblioteca CollectionUtils de Apache , podemos consultar este breve tutorial donde cubrimos sus usos.
En este tutorial, sin embargo, nos centraremos en su implementación filter () .
4.1. Dependencias
Primero, necesitaremos las siguientes dependencias en nuestro archivo pom.xml :
org.apache.commons commons-collections4 4.2
4.2. Filtering a Collection with CollectionUtils
We are now ready to use the CollectonUtils‘ methods:
public Collection findEvenNumbers(Collection baseCollection) { Predicate apachePredicate = item -> item % 2 == 0; CollectionUtils.filter(baseCollection, apachePredicate); return baseCollection; }
We have to take into account that this method modifies the baseCollection by removing every item that doesn't match the condition.
This means that the base Collection has to be mutable, otherwise it will throw an exception.
5. Using Guava's Collections2
As before, we can read our previous post ‘Filtering and Transforming Collections in Guava' for further information on this subject.
5.1. Dependencies
Let's start by adding this dependency in our pom.xml file:
com.google.guava guava 25.1-jre
5.2. Filtering a Collection with Collections2
As we can see, this approach is fairly similar to the one followed in the last section:
public Collection findEvenNumbers(Collection baseCollection) { Predicate guavaPredicate = item -> item % 2 == 0; return Collections2.filter(baseCollection, guavaPredicate); }
Again, here we define a Guava specific Predicate object.
In this case, Guava doesn't modify the baseCollection, it generates a new one, so we can use an immutable collection as input.
6. Conclusion
In summary, we've seen that there are many different ways of filtering collections in Java.
Even though Streams are usually the preferred approach, its good to know and keep in mind the functionality offered by other libraries.
Especially if we need to support older Java versions. However, if this is the case, we need to keep in mind recent Java features used throughout the tutorial such as lambdas should be replaced with anonymous classes.
Como es habitual, podemos encontrar todos los ejemplos que se muestran en este tutorial en nuestro repositorio de Github.