Filtro Java Stream con espressione Lambda

1. Introduzione

In questo rapido tutorial, esploreremo l'uso del metodo Stream.filter () quando lavoriamo con Streams in Java.

Vedremo come usarlo e come gestire casi speciali con eccezioni controllate.

2. Utilizzo di Stream.filter ()

Il metodo filter () è un'operazione intermedia dell'interfaccia Stream che ci permette di filtrare gli elementi di uno stream che corrispondono a un dato Predicate:

Stream filter(Predicate predicate)

Per vedere come funziona, creiamo una classe Customer :

public class Customer { private String name; private int points; //Constructor and standard getters }

Inoltre, creiamo una raccolta di clienti:

Customer john = new Customer("John P.", 15); Customer sarah = new Customer("Sarah M.", 200); Customer charles = new Customer("Charles B.", 150); Customer mary = new Customer("Mary T.", 1); List customers = Arrays.asList(john, sarah, charles, mary);

2.1. Filtraggio delle raccolte

Un caso d'uso comune del metodo filter () è l'elaborazione delle raccolte.

Facciamo un elenco di clienti con più di 100 punti. Per fare ciò, possiamo usare un'espressione lambda:

List customersWithMoreThan100Points = customers .stream() .filter(c -> c.getPoints() > 100) .collect(Collectors.toList());

Possiamo anche usare un metodo di riferimento, che è una scorciatoia per un'espressione lambda:

List customersWithMoreThan100Points = customers .stream() .filter(Customer::hasOverHundredPoints) .collect(Collectors.toList());

In questo caso, abbiamo aggiunto il metodo hasOverHundredPoints alla nostra classe Customer :

public boolean hasOverHundredPoints() { return this.points > 100; }

In entrambi i casi otteniamo lo stesso risultato:

assertThat(customersWithMoreThan100Points).hasSize(2); assertThat(customersWithMoreThan100Points).contains(sarah, charles);

2.2. Filtraggio di raccolte con più criteri

Inoltre, possiamo usare più condizioni con filter () . Ad esempio, possiamo filtrare per punti e nome :

List charlesWithMoreThan100Points = customers .stream() .filter(c -> c.getPoints() > 100 && c.getName().startsWith("Charles")) .collect(Collectors.toList()); assertThat(charlesWithMoreThan100Points).hasSize(1); assertThat(charlesWithMoreThan100Points).contains(charles);

3. Gestione delle eccezioni

Fino ad ora, abbiamo utilizzato il filtro con predicati che non generano un'eccezione. In effetti, le interfacce funzionali in Java non dichiarano eccezioni selezionate o deselezionate .

Successivamente mostreremo alcuni modi diversi per gestire le eccezioni nelle espressioni lambda.

3.1. Utilizzo di un wrapper personalizzato

Innanzitutto, inizieremo aggiungendo un profiloPhotoUrl al nostro cliente :

private String profilePhotoUrl;

Inoltre, aggiungiamo un semplice metodo hasValidProfilePhoto () per verificare la disponibilità del profilo:

public boolean hasValidProfilePhoto() throws IOException { URL url = new URL(this.profilePhotoUrl); HttpsURLConnection connection = (HttpsURLConnection) url.openConnection(); return connection.getResponseCode() == HttpURLConnection.HTTP_OK; }

Possiamo vedere che il metodo hasValidProfilePhoto () genera un'IOException . Ora se proviamo a filtrare i clienti con questo metodo:

List customersWithValidProfilePhoto = customers .stream() .filter(Customer::hasValidProfilePhoto) .collect(Collectors.toList());

Vedremo il seguente errore:

Incompatible thrown types java.io.IOException in functional expression

Per gestirlo, una delle alternative che possiamo usare è avvolgerlo con un blocco try-catch:

List customersWithValidProfilePhoto = customers .stream() .filter(c -> { try { return c.hasValidProfilePhoto(); } catch (IOException e) { //handle exception } return false; }) .collect(Collectors.toList());

Se abbiamo bisogno di lanciare un'eccezione dal nostro predicato, possiamo racchiuderla in un'eccezione non controllata come RuntimeException .

3.2. Utilizzo di ThrowingFunction

In alternativa, possiamo usare la libreria ThrowingFunction.

ThrowingFunction è una libreria open source che ci consente di gestire le eccezioni controllate nelle interfacce funzionali Java.

Cominciamo aggiungendo la dipendenza della funzione di lancio al nostro pom:

 pl.touk throwing-function 1.3 

Per gestire le eccezioni nei predicati, questa libreria ci offre la classe ThrowingPredicate , che ha il metodo unchecked () per racchiudere le eccezioni verificate.

Vediamolo in azione:

List customersWithValidProfilePhoto = customers .stream() .filter(ThrowingPredicate.unchecked(Customer::hasValidProfilePhoto)) .collect(Collectors.toList());

4. Conclusione

In questo articolo, abbiamo visto un esempio di come utilizzare il metodo filter () per elaborare i flussi. Abbiamo anche esplorato alcune alternative per gestire le eccezioni.

Come sempre, il codice completo è disponibile su GitHub.