Come filtrare una raccolta in Java

1. Panoramica

In questo breve tutorial, daremo uno sguardo ai diversi modi di filtrare una raccolta in Java , ovvero trovare tutti gli elementi che soddisfano una determinata condizione.

Questo è un compito fondamentale che è presente praticamente in qualsiasi applicazione Java.

Per questo motivo, il numero di librerie che forniscono funzionalità per questo scopo è significativo.

In particolare, in questo tutorial tratteremo:

  • Funzione filter () di Java 8 Streams
  • Raccoglitore di filtri Java 9
  • API delle raccolte Eclipse pertinenti
  • Metodo CollectionUtils filter () di Apache
  • L' approccio di Guava Collections2 filter ()

2. Utilizzo di flussi

Dall'introduzione di Java 8, gli stream hanno acquisito un ruolo chiave nella maggior parte dei casi in cui dobbiamo elaborare una raccolta di dati.

Di conseguenza, questo è l'approccio preferito nella maggior parte dei casi poiché è costruito in Java e non richiede dipendenze aggiuntive.

2.1. Filtro di una raccolta con flussi

Per semplicità, in tutti gli esempi il nostro obiettivo sarà creare un metodo che recuperi solo i numeri pari da una raccolta di valori Integer .

Pertanto, possiamo esprimere la condizione che useremo per valutare ogni elemento come " valore% 2 == 0 ".

In tutti i casi, dovremo definire questa condizione come un oggetto Predicate :

public Collection findEvenNumbers(Collection baseCollection) { Predicate streamsPredicate = item -> item % 2 == 0; return baseCollection.stream() .filter(streamsPredicate) .collect(Collectors.toList()); }

È importante notare che ogni libreria che analizziamo in questo tutorial fornisce la propria implementazione di Predicate , ma che comunque tutte sono definite come interfacce funzionali, permettendoci quindi di utilizzare le funzioni Lambda per dichiararle.

In questo caso, abbiamo utilizzato un Collector predefinito fornito da Java che accumula gli elementi in un List , ma avremmo potuto usarne altri, come discusso in questo post precedente.

2.2. Filtraggio dopo aver raggruppato una raccolta in Java 9

I flussi ci consentono di aggregare elementi utilizzando il raccoglitore groupingBy .

Tuttavia, se filtriamo come abbiamo fatto nell'ultima sezione, alcuni elementi potrebbero essere scartati in una fase iniziale, prima che questo collezionista entri in gioco.

Per questo motivo, con Java 9 è stato introdotto il filtering collector, con l'obiettivo di elaborare le sottoraccolte dopo che sono state raggruppate.

Seguendo il nostro esempio, immaginiamo di voler raggruppare la nostra raccolta in base al numero di cifre di ogni Integer, prima di filtrare i numeri dispari:

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()))); }
    

In breve, se usiamo questo raccoglitore, potremmo ritrovarci con una voce di valore vuota, mentre se filtriamo prima di raggruppare, il raccoglitore non creerebbe affatto una tale voce.

Naturalmente, sceglieremmo l'approccio in base alle nostre esigenze.

3. Utilizzo delle collezioni Eclipse

Possiamo anche fare uso di altre librerie di terze parti per raggiungere il nostro obiettivo, sia perché la nostra applicazione non supporta Java 8 o perché vogliamo sfruttare alcune potenti funzionalità non fornite da Java.

È il caso di Eclipse Collections , una libreria che si sforza di stare al passo con i nuovi paradigmi, evolvendosi e abbracciando i cambiamenti introdotti da tutte le ultime versioni di Java.

Possiamo iniziare esplorando il nostro post introduttivo sulle collezioni Eclipse per avere una conoscenza più ampia delle funzionalità fornite da questa libreria.

3.1. Dipendenze

Cominciamo aggiungendo la seguente dipendenza al pom.xml del nostro progetto :

 org.eclipse.collections eclipse-collections 9.2.0 

Le raccolte eclipse includono tutte le interfacce della struttura dati necessarie e l'API stessa.

3.2. Filtraggio di una raccolta con le raccolte Eclipse

Usiamo ora la funzionalità di filtro di eclipse su una delle sue strutture di dati, come la sua MutableList :

public Collection findEvenNumbers(Collection baseCollection) { Predicate eclipsePredicate = item -> item % 2 == 0; Collection filteredList = Lists.mutable .ofAll(baseCollection) .select(eclipsePredicate); return filteredList; }

In alternativa, potremmo aver usato l'Iterazione s' select ()metodo statico per definire l' oggetto filteredList :

Collection filteredList = Iterate.select(baseCollection, eclipsePredicate);

4. Utilizzo di CollectionUtils di Apache

Per iniziare con la libreria CollectionUtils di Apache , possiamo dare un'occhiata a questo breve tutorial in cui abbiamo coperto i suoi usi.

In questo tutorial, tuttavia, ci concentreremo sulla sua implementazione di filter () .

4.1. Dipendenze

Innanzitutto, avremo bisogno delle seguenti dipendenze nel nostro file 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.

Come al solito, possiamo trovare tutti gli esempi mostrati in questo tutorial nel nostro repository Github.