DistinctBy nell'API Java Stream

1. Panoramica

La ricerca di elementi diversi in un elenco è una delle attività comuni che di solito noi programmatori affrontiamo. Da Java 8 in poi con l'inclusione di Streams abbiamo una nuova API per elaborare i dati utilizzando un approccio funzionale.

In questo articolo, mostreremo diverse alternative al filtraggio di una raccolta utilizzando un particolare attributo di oggetti nell'elenco.

2. Utilizzo dell'API Stream

L'API Stream fornisce il metodo Different () che restituisce diversi elementi di un elenco in base al metodo equals () della classe Object .

Tuttavia, diventa meno flessibile se vogliamo filtrare in base a un attributo specifico. Una delle alternative che abbiamo è scrivere un filtro che mantenga lo stato.

2.1. Utilizzo di un filtro con stato

Una delle possibili soluzioni sarebbe implementare un predicato stateful :

public static  Predicate distinctByKey( Function keyExtractor) { Map seen = new ConcurrentHashMap(); return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null; }

Per testarlo, useremo la seguente classe Person che ha gli attributi age , email e name:

public class Person { private int age; private String name; private String email; // standard getters and setters }

E per ottenere una nuova raccolta filtrata in base al nome , possiamo utilizzare:

List personListFiltered = personList.stream() .filter(distinctByKey(p -> p.getName())) .collect(Collectors.toList());

3. Utilizzo delle collezioni Eclipse

Eclipse Collections è una libreria che fornisce metodi aggiuntivi per l'elaborazione di stream e raccolte in Java.

3.1. Utilizzo di ListIterate.distinct ()

Il metodo ListIterate.distinct () ci consente di filtrare uno Stream utilizzando varie HashingStrategies. Queste strategie possono essere definite utilizzando espressioni lambda o riferimenti a metodi.

Se vogliamo filtrare in base al nome della persona :

List personListFiltered = ListIterate .distinct(personList, HashingStrategies.fromFunction(Person::getName));

Oppure, se l'attributo che useremo è primitivo (int, long, double), possiamo usare una funzione specializzata come questa:

List personListFiltered = ListIterate.distinct( personList, HashingStrategies.fromIntFunction(Person::getAge));

3.2. Dipendenza da Maven

Dobbiamo aggiungere le seguenti dipendenze al nostro pom.xml per utilizzare le collezioni Eclipse nel nostro progetto:

 org.eclipse.collections eclipse-collections 8.2.0 

È possibile trovare l'ultima versione della libreria Eclipse Collections nel repository Maven Central.

Per saperne di più su questa libreria possiamo andare a questo articolo.

4. Utilizzo di Vavr (J avaslang )

Questa è una libreria funzionale per Java 8 che fornisce dati immutabili e strutture di controllo funzionale.

4.1. Utilizzando List.distinctBy

Per filtrare le liste, questa classe fornisce la propria classe List che ha il metodo separateBy () che ci permette di filtrare per attributi degli oggetti che contiene:

List personListFiltered = List.ofAll(personList) .distinctBy(Person::getName) .toJavaList();

4.2. Dipendenza da Maven

Aggiungeremo le seguenti dipendenze al nostro pom.xml per utilizzare Vavr nel nostro progetto.

 io.vavr vavr 0.9.0 

È possibile trovare l'ultima versione della libreria Vavr nel repository Maven Central.

Per saperne di più su questa libreria possiamo andare a questo articolo.

5. Utilizzo di StreamEx

Questa libreria fornisce classi e metodi utili per l'elaborazione dei flussi Java 8.

5.1. Utilizzando StreamEx.distinct

All'interno delle classi fornite c'è StreamEx che ha il metodo distinto a cui possiamo inviare un riferimento all'attributo in cui vogliamo distinguere:

List personListFiltered = StreamEx.of(personList) .distinct(Person::getName) .toList();

5.2. Dipendenza da Maven

Aggiungeremo le seguenti dipendenze al nostro pom.xml per utilizzare StreamEx nel nostro progetto.

 one.util streamex 0.6.5 

È possibile trovare l'ultima versione della libreria StreamEx nel repository Maven Central.

6. Conclusione

In questo rapido tutorial, abbiamo esplorato esempi di come ottenere diversi elementi di un flusso, in base a un attributo utilizzando l'API Java 8 standard e alternative aggiuntive con altre librerie.

Come sempre, il codice completo è disponibile su GitHub.