Gli Stream Collector sono una potente fun­zio­na­li­tà dell’API Stream di Java 8, che permette di rac­co­glie­re ed elaborare i dati in modo ef­fi­cien­te. Ti spie­ghia­mo la struttura e le pos­si­bi­li­tà di ap­pli­ca­zio­ne del metodo collect in Java.

Quali sono i campi di ap­pli­ca­zio­ne per Java Collect()?

Uno Stream Collector può essere uti­liz­za­to per creare una lista, un set o una mappa partendo da uno stream. Una mappa è una sequenza di elementi che vengono elaborati uno dopo l’altro. L’in­ter­fac­cia Collector offre una serie di ope­ra­zio­ni di riduzione per i dati in una pipeline di stream. Si tratta di ope­ra­zio­ni da terminale che rac­col­go­no e uniscono i risultati di passaggi intermedi.

Ad esempio, i Collector possono essere uti­liz­za­ti per filtrare o ordinare gli oggetti di uno stream. È possibile eseguire anche l’ope­ra­zio­ne di ag­gre­ga­zio­ne, ossia la somma di numeri, l’unione di stringhe o il conteggio di elementi. Inoltre, i Collector di­spon­go­no di funzioni per tra­sfor­ma­re i contenuti di uno stream in una data struttura. In questo modo è possibile, ad esempio, con­ver­ti­re una lista in una mappa. I rag­grup­pa­men­ti aiutano a ca­te­go­riz­za­re gli elementi con de­ter­mi­na­te ca­rat­te­ri­sti­che o con­di­zio­ni. Ma, so­prat­tut­to, gli Stream Collector hanno il vantaggio di elaborare i dati in parallelo per mezzo di più thread. In questo modo per­met­to­no di eseguire le ope­ra­zio­ni molto più ve­lo­ce­men­te e con molta più ef­fi­cien­za, in par­ti­co­la­re in presenza di grandi quantità di dati.

La sintassi di Java Collect()

Questo metodo accetta un Collector come argomento che descrive il modo in cui gli elementi dello stream devono essere raccolti e aggregati. Un Collector è un’in­ter­fac­cia che mette a di­spo­si­zio­ne diversi metodi per unire gli elementi dello stream in un data forma, ad esempio in una lista, un set o una mappa.

Esistono due tipi di metodi Java Collect:

  1. <R> R collect(Supplier<R> supplier, Bi­Con­su­mer<R, ? super T> ac­cu­mu­la­tor,Bi­Con­su­mer<R, R> combiner)
  2. <R, A> R collect(Collector<? super T, A, R> collector)

La prima variante ha tre funzioni come argomento:

  • supplier: crea un con­te­ni­to­re uti­liz­za­to per i risultati intermedi.
  • ac­cu­mu­la­tor: calcola il risultato finale.
  • combiner: combina i risultati di ope­ra­zio­ni di stream parallele.

Questi Collector pre­de­fi­ni­ti sono già inclusi nella libreria standard e possono essere importati e uti­liz­za­ti fa­cil­men­te.

La seconda variante utilizza come argomento un Collector e re­sti­tui­sce il risultato.

  • R: il tipo di risultato.
  • T: il tipo degli elementi nello stream.
  • A: il tipo dell’ac­cu­mu­la­to­re che memorizza lo stato in­ter­me­dio dell’ope­ra­zio­ne Collector.
  • collector: esegue l’ope­ra­zio­ne di riduzione.

Uti­liz­zan­do questa variante, gli svi­lup­pa­to­ri possono creare Collector su misura pensati ap­po­si­ta­men­te per le loro esigenze, offrendo maggiore fles­si­bi­li­tà e controllo sul processo di riduzione.

Esempi pratici di utilizzo di Java Collect()

Nelle parti seguenti ti mostriamo diverse funzioni del metodo Stream.collect(). Prima di iniziare a usare il Col­lec­tion Framework, ti con­si­glia­mo di prendere di­me­sti­chez­za con gli operatori Java.

Con­ca­te­na­zio­ne di una lista di stringhe

Con Java Collect() puoi con­ca­te­na­re una lista di stringhe per ottenere una nuova stringa:

List<String> letters = List.of("a", "b", "c", "d", "e");
// without combiner function
StringBuilder result = letters.stream().collect(StringBuilder::new, (x, y) -> x.append(y),
    (a, b) -> a.append(",").append(b));
System.out.println(result.toString());
// with combiner function
StringBuilder result1 = letters.parallelStream().collect(StringBuilder::new, (x, y) -> x.append(y),
    (a, b) -> a.append(",").append(b));
System.out.println(result1.toString());
Java

Il risultato che otteniamo è il seguente:

abcde
a, b, c, d, e
Java

Con il primo calcolo è presente soltanto un’istanza StringBuilder e non è stata uti­liz­za­ta alcuna funzione Combiner. Per questo motivo il risultato è abcde.

Il secondo output mostra come la funzione Combiner unisce le istanze StringBuilder e le separa con una virgola.

Raccolta di elementi in una lista con toList()

È possibile uti­liz­za­re la funzione filter() per se­le­zio­na­re de­ter­mi­na­ti elementi e me­mo­riz­zar­li in una nuova lista con toList().

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
List<Integer> oddNumbers = numbers.stream().filter(x -> x % 2 != 0).collect(Collectors.toList());
System.out.println(oddNumbers);
Java

La nuova lista contiene quindi esclu­si­va­men­te numeri dispari:

[1, 3, 5, 7]
Java

Raccolta di elementi in un set con toSet()

Ana­lo­ga­men­te è possibile creare un nuovo set partendo da elementi se­le­zio­na­ti. La sequenza può essere non ordinata in un set.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Set<Integer> evenNumbers = numbers.parallelStream().filter(x -> x % 2 == 0).collect(Collectors.toSet());
System.out.println(evenNumbers);
Java

Il risultato è il seguente:

[2, 4, 6]
Java

Raccolta di elementi in una mappa con toMap()

L’uso di una mappa in com­bi­na­zio­ne con Java Collect() permette di assegnare un valore a ciascuna chiave.

List<Integer> numbers = List.of(1, 2, 3, 4, 5, 6, 7);
Map<Integer, String> mapEvenNumbers = numbers.parallelStream().filter(x -> x % 2 == 0)
    .collect(Collectors.toMap(Function.identity(), x -> String.valueOf(x)));
System.out.println(mapEvenNumbers);
Java

Il risultato mostra come l’input, composto da numeri pari, è stato assegnato ai suoi valori identici:

{2=2, 4=4, 6=6}
Java

Com­bi­na­zio­ne di elementi in una catena di caratteri con joining()

Il metodo joining() combina ciascun elemento dello stream nell’ordine in cui appare e utilizza un carattere se­pa­ra­to­re per separare gli elementi. Il se­pa­ra­to­re può essere passato a joining() come argomento. Se non si indica nessun se­pa­ra­to­re, joining() utilizza la stringa vuota "".

jshell> String result1 = Stream.of("a", "b", "c").collect(Collectors.joining());
jshell> String result2 = Stream.of("a", "b", "c").collect(Collectors.joining(",", "{", "}"));
Java

I risultati sono:

result1 ==> "abc"
result2 ==> "{a,b,c}"
Java
Vai al menu prin­ci­pa­le