Stream Col­lec­tors são um recurso poderoso da API Java 8 Stream, capaz de coletar e processar dados de maneira eficiente. Conheça sua estrutura e como o método Java collect() pode ser utilizado.

Como usar Java collect()?

Um Stream Collector pode ser usado para criar uma lista, um conjunto ou um mapa a partir de um stream. Um stream, por sua vez, é uma sequência de elementos pro­ces­sa­dos um após o outro. A interface Collector oferece um conjunto de operações de redução para dados em um pipeline de stream, ou seja, operações terminais que coletam e mesclam re­sul­ta­dos de etapas in­ter­me­diá­rias.

Col­lec­tors podem ser usados para filtrar ou ordenar objetos de um stream. Agre­ga­ções também são possíveis, como somar números, combinar strings ou contar elementos. Além disso, Col­lec­tors possuem funções que podem trans­for­mar o conteúdo de um stream em uma estrutura es­pe­cí­fica. Você pode, por exemplo, trans­for­mar uma lista em um mapa. Ainda, agru­pa­men­tos ajudam a ca­te­go­ri­zar elementos com certas pro­pri­e­da­des ou condições.

O mais im­por­tante é que Stream Col­lec­tors têm a vantagem de processar dados si­mul­ta­ne­a­mente, usando vários threads. Isso permite que operações sejam re­a­li­za­das muito mais ra­pi­da­mente e de forma eficiente, es­pe­ci­al­mente em grandes quan­ti­da­des de dados.

Qual é a sintaxe do Java collect()?

O método aceita um Collector como argumento, que descreve como os elementos do stream devem ser coletados e agregados. Collector é uma interface que fornece vários métodos para agregar elementos do stream de uma forma es­pe­cí­fica, como em uma lista, um conjunto ou um mapa.

Existem duas variantes do método Java Stream 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)

A primeira variante tem três funções como ar­gu­men­tos:

  • supplier: Cria um re­ci­pi­ente que será usado para re­sul­ta­dos in­ter­me­diá­rios.
  • ac­cu­mu­la­tor: Calcula o resultado final.
  • combiner: Combina os re­sul­ta­dos de operações de stream paralelos.

Esses Col­lec­tors pre­de­fi­ni­dos já estão incluídos na bi­bli­o­teca padrão e podem ser fa­cil­mente im­por­ta­dos e usados.

A segunda variante aceita um Collector como argumento e retorna um resultado:

  • R: O tipo de resultado.
  • T: O tipo de elementos no stream.
  • A: O tipo de acu­mu­la­dor que armazena o estado in­ter­me­diá­rio da operação do collector.
  • collector: Executa a operação de redução.

Usando essa variante, os de­sen­vol­ve­do­res podem criar Col­lec­tors per­so­na­li­za­dos, que são es­pe­ci­fi­ca­mente adaptados às suas ne­ces­si­da­des, pro­por­ci­o­nando maior fle­xi­bi­li­dade e controle sobre o processo de redução.

Exemplos práticos do uso do Java collect()

Abaixo, ilus­tra­mos várias funções do método Stream.collect(). Você deve se fa­mi­li­a­ri­zar com os ope­ra­do­res em Java básicos antes de se apro­fun­dar no framework de coleção.

Con­ca­te­nar lista de strings

Com Java collect(), podemos con­ca­te­nar uma lista de strings para obtermos uma nova string:

List<String> letras = List.of("a", "b", "c", "d", "e");
// sem função combiner
StringBuilder resultado = letras.stream().collect(StringBuilder::new, (x, y) -> x.append(y),
    (a, b) -> a.append("").append(b));
System.out.println(resultado.toString());
// com função combiner
StringBuilder resultado1 = letras.parallelStream().collect(StringBuilder::new, (x, y) -> x.append(y),
    (a, b) -> a.append("").append(b));
System.out.println(resultado1.toString());
Java

A saída é:

abcde
a b c d e
Java

No primeiro cálculo, foi usada apenas uma instância de StringBuilder e nenhuma função combiner. Jus­ta­mente por isso que o resultado exibido foi abcde.

Na segunda saída, a função combiner mesclou as ins­tân­cias de StringBuilder e as separou por vírgula.

Coletar elementos em uma lista com toList()

Podemos usar a função filter() para se­le­ci­o­nar certos elementos de uma lista e, em seguida, usar toList() para armazená-los em uma nova lista.

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

A nova lista tem apenas números ímpares:

[1, 3, 5, 7]
Java

Coletar elementos em um conjunto com toSet()

Da mesma forma, podemos se­le­ci­o­nar elementos para criar um novo conjunto a partir deles. Elementos em um conjunto não precisam ser dispostos em uma ordem es­pe­cí­fica.

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

O código exibe a seguinte saída:

[2, 4, 6]
Java

Coletar elementos em um mapa com toMap()

Um mapa pode ser usado jun­ta­mente com o Java collect() para atribuir um valor a cada chave.

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

Na saída, podemos observar que cada número par de entrada foi atribuído a um valor idêntico:

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

Combinar elementos em uma string com joining()

O método joining() combina elementos de um stream na ordem em que aparecem, usando um separador para separar os elementos. O separador é passado como argumento para joining(). Se nenhum separador for es­pe­ci­fi­cado, joining() usará a string vazia "".

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

Os re­sul­ta­dos obtidos são:

resultado1 ==> "abc"
resultado2 ==> "{abc}"
Java
Ir para o menu principal