Notice: This page requires JavaScript to function properly.
Please enable JavaScript in your browser settings or update your browser.
Aprenda Collect() Reunindo Elementos de Stream em uma Coleção | Operações Terminais na Stream API
Stream API

bookCollect() Reunindo Elementos de Stream em uma Coleção

Você já está familiarizado com operações terminais e até mesmo as utilizou em exemplos e exercícios anteriores. Agora é o momento de analisar mais detalhadamente como elas funcionam. O primeiro método a ser abordado é o collect(), que é uma das principais operações terminais na Stream API.

O método collect()

É uma das ferramentas mais poderosas ao trabalhar com streams, permitindo acumular resultados em um List, Set ou Map, além de realizar agrupamentos complexos e cálculos estatísticos.

Existem duas implementações do método collect()—vamos explorar ambas.

Utilizando collect() com Interfaces Funcionais

O método collect() na Stream API pode ser utilizado com três interfaces funcionais para fornecer controle total sobre a coleta de dados:

  • Supplier<R> supplier – cria uma coleção vazia (R) onde os elementos serão armazenados. Por exemplo, ArrayList::new inicializa uma nova lista;
  • BiConsumer<R, ? super T> accumulator – adiciona elementos do stream (T) à coleção (R). Por exemplo, List::add insere itens em uma lista;
  • BiConsumer<R, R> combiner – mescla duas coleções quando é utilizado processamento paralelo. Por exemplo, List::addAll combina listas em uma só.

Os três componentes trabalham juntos para proporcionar flexibilidade na coleta de dados. Primeiro, o supplier cria uma coleção vazia que será utilizada para acumular os elementos do stream. Em seguida, o accumulator adiciona cada elemento conforme o stream os processa. Esse fluxo permanece simples em um stream sequencial.

No entanto, ao trabalhar com streams paralelos (parallelStream()), o processo se torna mais complexo.

O processamento de dados é distribuído entre múltiplas threads, com cada thread criando sua própria coleção separada. Após a conclusão do processamento, essas coleções individuais precisam ser mescladas em um único resultado. É nesse momento que o combiner atua, combinando de forma eficiente as partes separadas em uma coleção unificada.

Exemplo Prático

Você trabalha em uma loja online e possui uma lista de produtos. Sua tarefa é coletar apenas os produtos que custam mais de $500 utilizando o método collect() com três parâmetros.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Initial list of products List<Product> productList = List.of( new Product("Laptop", 1200.99), new Product("Phone", 599.49), new Product("Headphones", 199.99), new Product("Monitor", 299.99), new Product("Tablet", 699.99) ); // Filtering and collecting products over $500 using `collect()` List<Product> expensiveProducts = productList.parallelStream() .filter(product -> product.getPrice() > 500) // Keep only expensive products .collect( ArrayList::new, // Create a new list (list, product) -> list.add(product), // Add each product to the list ArrayList::addAll // Merge lists (if the stream is parallel) ); // Print the result System.out.print("Products over $500: " + expensiveProducts); } } class Product { private String name; private double price; Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }

O método collect() recebe três argumentos, cada um definindo uma etapa diferente na coleta de elementos em uma lista:

  • ArrayList::new (Supplier) → cria um ArrayList<Product> vazio para armazenar os resultados;

  • (list, product) -> list.add(product) (BiConsumer) → adiciona cada Product à lista se atender à condição de filtro (price > 500);

  • ArrayList::addAll (BiConsumer) → mescla múltiplas listas ao utilizar streams paralelos, garantindo que todos os produtos filtrados sejam combinados em uma única lista.

Embora o terceiro parâmetro seja utilizado principalmente para processamento paralelo, ele é obrigatório no collect().

Utilizando collect() com a Interface Collector

Além de trabalhar com três interfaces funcionais, o método collect() na Stream API também pode ser utilizado com implementações predefinidas da interface Collector.

Essa abordagem é mais flexível e conveniente, pois oferece métodos integrados para trabalhar com coleções.

A interface Collector<T, A, R> consiste em vários métodos principais:

  • Supplier<A> supplier() – cria um container vazio para acumular elementos;
  • BiConsumer<A, T> accumulator() – define como os elementos são adicionados ao container;
  • BinaryOperator<A> combiner() – mescla dois containers quando é utilizado processamento paralelo;
  • Function<A, R> finisher() – transforma o container no resultado final.

Como pode ser observado, essa estrutura é semelhante ao método collect() que trabalha com interfaces funcionais, mas introduz o método finisher(). Essa etapa adicional permite um processamento extra nos dados coletados antes de retornar o resultado final—por exemplo, ordenar a lista antes de retorná-la.

Além disso, a interface Collector fornece o método characteristics(), que define propriedades que ajudam a otimizar a execução do stream:

Essas características ajudam a Stream API a otimizar o desempenho. Por exemplo, se uma coleção é inerentemente não ordenada, especificar UNORDERED pode evitar ordenações desnecessárias, tornando a operação mais eficiente.

Exemplo Prático

Imagine uma loja online em que seja necessário processar os preços dos produtos antes de coletá-los. Por exemplo, deseja-se arredondar cada preço para o número inteiro mais próximo, remover duplicatas e ordenar a lista final.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package com.example; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<Double> prices = List.of(1200.99, 599.49, 199.99, 599.49, 1200.49, 200.0); // Using a custom `Collector` to round prices, remove duplicates, and sort List<Integer> processedPrices = prices.parallelStream() .collect(new RoundedSortedCollector()); System.out.println("Processed product prices: " + processedPrices); } } // Custom `Collector` that rounds prices, removes duplicates, and sorts them class RoundedSortedCollector implements Collector<Double, Set<Integer>, List<Integer>> { @Override public Supplier<Set<Integer>> supplier() { // Creates a `HashSet` to store unique rounded values return HashSet::new; } @Override public BiConsumer<Set<Integer>, Double> accumulator() { // Rounds price and adds to the set return (set, price) -> set.add((int) Math.round(price)); } @Override public BinaryOperator<Set<Integer>> combiner() { return (set1, set2) -> { set1.addAll(set2); // Merges two sets return set1; }; } @Override public Function<Set<Integer>, List<Integer>> finisher() { return set -> set.stream() .sorted() // Sorts the final list .toList(); } @Override public Set<Characteristics> characteristics() { // Order is not important during accumulation return Set.of(Characteristics.UNORDERED); } }

O processamento dos dados começa ao passá-los para um Collector personalizado chamado RoundedSortedCollector.

Este coletor acumula todos os preços em um Set<Integer>, garantindo que duplicatas sejam automaticamente removidas. Antes de adicionar cada valor, ele arredonda o preço usando Math.round(price) e converte para int. Por exemplo, tanto 1200.99 quanto 1200.49 se tornam 1200, enquanto 199.99 é arredondado para 200.

Se o stream for executado em modo paralelo, o método combiner() mescla dois conjuntos adicionando todos os elementos de um conjunto ao outro. Esta etapa é fundamental para ambientes multithread.

Na etapa final, após todos os preços serem coletados, o método finisher() transforma o conjunto em uma lista ordenada. Ele converte o Set<Integer> em um stream, aplica sorted() para organizar os valores em ordem crescente e, em seguida, coleta-os em uma List<Integer>.

Como resultado, obtém-se uma lista ordenada de preços únicos e arredondados que pode ser utilizada para cálculos ou exibição.

1. O que o método collect() faz na Stream API?

2. Qual capacidade adicional a interface Collector fornece em comparação ao uso de collect() com interfaces funcionais?

question mark

O que o método collect() faz na Stream API?

Select the correct answer

question mark

Qual capacidade adicional a interface Collector fornece em comparação ao uso de collect() com interfaces funcionais?

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 3. Capítulo 1

Pergunte à IA

expand

Pergunte à IA

ChatGPT

Pergunte o que quiser ou experimente uma das perguntas sugeridas para iniciar nosso bate-papo

Suggested prompts:

Can you explain the difference between using collect() with functional interfaces and with the Collector interface?

What are some common use cases for the collect() method in real-world applications?

Could you provide a simple code example demonstrating the use of collect() in Java?

Awesome!

Completion rate improved to 2.33

bookCollect() Reunindo Elementos de Stream em uma Coleção

Deslize para mostrar o menu

Você já está familiarizado com operações terminais e até mesmo as utilizou em exemplos e exercícios anteriores. Agora é o momento de analisar mais detalhadamente como elas funcionam. O primeiro método a ser abordado é o collect(), que é uma das principais operações terminais na Stream API.

O método collect()

É uma das ferramentas mais poderosas ao trabalhar com streams, permitindo acumular resultados em um List, Set ou Map, além de realizar agrupamentos complexos e cálculos estatísticos.

Existem duas implementações do método collect()—vamos explorar ambas.

Utilizando collect() com Interfaces Funcionais

O método collect() na Stream API pode ser utilizado com três interfaces funcionais para fornecer controle total sobre a coleta de dados:

  • Supplier<R> supplier – cria uma coleção vazia (R) onde os elementos serão armazenados. Por exemplo, ArrayList::new inicializa uma nova lista;
  • BiConsumer<R, ? super T> accumulator – adiciona elementos do stream (T) à coleção (R). Por exemplo, List::add insere itens em uma lista;
  • BiConsumer<R, R> combiner – mescla duas coleções quando é utilizado processamento paralelo. Por exemplo, List::addAll combina listas em uma só.

Os três componentes trabalham juntos para proporcionar flexibilidade na coleta de dados. Primeiro, o supplier cria uma coleção vazia que será utilizada para acumular os elementos do stream. Em seguida, o accumulator adiciona cada elemento conforme o stream os processa. Esse fluxo permanece simples em um stream sequencial.

No entanto, ao trabalhar com streams paralelos (parallelStream()), o processo se torna mais complexo.

O processamento de dados é distribuído entre múltiplas threads, com cada thread criando sua própria coleção separada. Após a conclusão do processamento, essas coleções individuais precisam ser mescladas em um único resultado. É nesse momento que o combiner atua, combinando de forma eficiente as partes separadas em uma coleção unificada.

Exemplo Prático

Você trabalha em uma loja online e possui uma lista de produtos. Sua tarefa é coletar apenas os produtos que custam mais de $500 utilizando o método collect() com três parâmetros.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152
package com.example; import java.util.ArrayList; import java.util.List; public class Main { public static void main(String[] args) { // Initial list of products List<Product> productList = List.of( new Product("Laptop", 1200.99), new Product("Phone", 599.49), new Product("Headphones", 199.99), new Product("Monitor", 299.99), new Product("Tablet", 699.99) ); // Filtering and collecting products over $500 using `collect()` List<Product> expensiveProducts = productList.parallelStream() .filter(product -> product.getPrice() > 500) // Keep only expensive products .collect( ArrayList::new, // Create a new list (list, product) -> list.add(product), // Add each product to the list ArrayList::addAll // Merge lists (if the stream is parallel) ); // Print the result System.out.print("Products over $500: " + expensiveProducts); } } class Product { private String name; private double price; Product(String name, double price) { this.name = name; this.price = price; } public String getName() { return name; } public double getPrice() { return price; } @Override public String toString() { return name + " ($" + price + ")"; } }

O método collect() recebe três argumentos, cada um definindo uma etapa diferente na coleta de elementos em uma lista:

  • ArrayList::new (Supplier) → cria um ArrayList<Product> vazio para armazenar os resultados;

  • (list, product) -> list.add(product) (BiConsumer) → adiciona cada Product à lista se atender à condição de filtro (price > 500);

  • ArrayList::addAll (BiConsumer) → mescla múltiplas listas ao utilizar streams paralelos, garantindo que todos os produtos filtrados sejam combinados em uma única lista.

Embora o terceiro parâmetro seja utilizado principalmente para processamento paralelo, ele é obrigatório no collect().

Utilizando collect() com a Interface Collector

Além de trabalhar com três interfaces funcionais, o método collect() na Stream API também pode ser utilizado com implementações predefinidas da interface Collector.

Essa abordagem é mais flexível e conveniente, pois oferece métodos integrados para trabalhar com coleções.

A interface Collector<T, A, R> consiste em vários métodos principais:

  • Supplier<A> supplier() – cria um container vazio para acumular elementos;
  • BiConsumer<A, T> accumulator() – define como os elementos são adicionados ao container;
  • BinaryOperator<A> combiner() – mescla dois containers quando é utilizado processamento paralelo;
  • Function<A, R> finisher() – transforma o container no resultado final.

Como pode ser observado, essa estrutura é semelhante ao método collect() que trabalha com interfaces funcionais, mas introduz o método finisher(). Essa etapa adicional permite um processamento extra nos dados coletados antes de retornar o resultado final—por exemplo, ordenar a lista antes de retorná-la.

Além disso, a interface Collector fornece o método characteristics(), que define propriedades que ajudam a otimizar a execução do stream:

Essas características ajudam a Stream API a otimizar o desempenho. Por exemplo, se uma coleção é inerentemente não ordenada, especificar UNORDERED pode evitar ordenações desnecessárias, tornando a operação mais eficiente.

Exemplo Prático

Imagine uma loja online em que seja necessário processar os preços dos produtos antes de coletá-los. Por exemplo, deseja-se arredondar cada preço para o número inteiro mais próximo, remover duplicatas e ordenar a lista final.

Main.java

Main.java

copy
12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455
package com.example; import java.util.*; import java.util.function.*; import java.util.stream.Collector; import java.util.stream.Stream; public class Main { public static void main(String[] args) { List<Double> prices = List.of(1200.99, 599.49, 199.99, 599.49, 1200.49, 200.0); // Using a custom `Collector` to round prices, remove duplicates, and sort List<Integer> processedPrices = prices.parallelStream() .collect(new RoundedSortedCollector()); System.out.println("Processed product prices: " + processedPrices); } } // Custom `Collector` that rounds prices, removes duplicates, and sorts them class RoundedSortedCollector implements Collector<Double, Set<Integer>, List<Integer>> { @Override public Supplier<Set<Integer>> supplier() { // Creates a `HashSet` to store unique rounded values return HashSet::new; } @Override public BiConsumer<Set<Integer>, Double> accumulator() { // Rounds price and adds to the set return (set, price) -> set.add((int) Math.round(price)); } @Override public BinaryOperator<Set<Integer>> combiner() { return (set1, set2) -> { set1.addAll(set2); // Merges two sets return set1; }; } @Override public Function<Set<Integer>, List<Integer>> finisher() { return set -> set.stream() .sorted() // Sorts the final list .toList(); } @Override public Set<Characteristics> characteristics() { // Order is not important during accumulation return Set.of(Characteristics.UNORDERED); } }

O processamento dos dados começa ao passá-los para um Collector personalizado chamado RoundedSortedCollector.

Este coletor acumula todos os preços em um Set<Integer>, garantindo que duplicatas sejam automaticamente removidas. Antes de adicionar cada valor, ele arredonda o preço usando Math.round(price) e converte para int. Por exemplo, tanto 1200.99 quanto 1200.49 se tornam 1200, enquanto 199.99 é arredondado para 200.

Se o stream for executado em modo paralelo, o método combiner() mescla dois conjuntos adicionando todos os elementos de um conjunto ao outro. Esta etapa é fundamental para ambientes multithread.

Na etapa final, após todos os preços serem coletados, o método finisher() transforma o conjunto em uma lista ordenada. Ele converte o Set<Integer> em um stream, aplica sorted() para organizar os valores em ordem crescente e, em seguida, coleta-os em uma List<Integer>.

Como resultado, obtém-se uma lista ordenada de preços únicos e arredondados que pode ser utilizada para cálculos ou exibição.

1. O que o método collect() faz na Stream API?

2. Qual capacidade adicional a interface Collector fornece em comparação ao uso de collect() com interfaces funcionais?

question mark

O que o método collect() faz na Stream API?

Select the correct answer

question mark

Qual capacidade adicional a interface Collector fornece em comparação ao uso de collect() com interfaces funcionais?

Select the correct answer

Tudo estava claro?

Como podemos melhorá-lo?

Obrigado pelo seu feedback!

Seção 3. Capítulo 1
some-alt