Introdução ao Collectors
O Collectors é uma classe utilitária da API de Stream Java que fornece várias operações de redução, como acumulação de elementos em coleções, resumo de elementos de acordo com vários critérios e outras operações semelhantes.
Considere um cenário onde precisamos categorizar uma lista de objetos com base em uma propriedade. Por exemplo, separar produtos por sua cor.
package com.example.streams;
import java.util.*; import java.util.stream.Collectors;
public class CollectorBasico { public static void main(String[] args) { List<Produto> produtos = Arrays.asList( new Produto("verde", 150), new Produto("amarelo", 120), new Produto("verde", 170), new Produto("amarelo", 125) );
// Filtrar e coletar em uma nova lista
List<Produto> produtosVerdes = produtos.stream()
.filter(p -> p.cor().equals("verde"))
.collect(Collectors.toList());
// Agrupamento usando Collectors.groupingBy
Map<String, List<Produto>> produtosPorCor = produtos.stream()
.collect(Collectors.groupingBy(Produto::cor));
System.out.println("Produtos verdes: " + produtosVerdes);
System.out.println("Agrupados por cor: " + produtosPorCor);
}
}
record Produto(String cor, int peso) {}
</div>Principais Métodos de Coleta
----------------------------
A classe `Collectors` oferece métodos estáticos para diversas operações de coleta. Vamos explorar os mais comuns.
### Cálculos Estatísticos
Podemos calcular médias, somas e estatísticas reusmidas diretamente no stream.
<div class="code-example">```
package com.example.streams;
import java.util.*;
import java.util.stream.Collectors;
public class EstatisticasCollector {
public static void main(String[] args) {
List<Prato> cardapio = Arrays.asList(
new Prato("carne", false, 800),
new Prato("peixe", false, 400),
new Prato("salada", true, 150),
new Prato("macarrão", true, 550)
);
// Média de calorias
double mediaCalorias = cardapio.stream()
.collect(Collectors.averagingInt(Prato::calorias));
// Contagem de itens
long totalItens = cardapio.stream()
.collect(Collectors.counting());
// Estatísticas completas
IntSummaryStatistics stats = cardapio.stream()
.collect(Collectors.summarizingInt(Prato::calorias));
System.out.println("Média de calorias: " + mediaCalorias);
System.out.println("Total de itens: " + totalItens);
System.out.println("Resumo estatístico: " + stats);
}
}
record Prato(String nome, boolean vegetariano, int calorias) {}
Além do groupingBy simples, podemos combinar múltiplos collectors para operações complexas.
package com.example.streams;
import java.util.*; import java.util.stream.Collectors;
public class AgrupamentoAvancado { public static void main(String[] args) { List<Prato> cardapio = Arrays.asList( new Prato("carne", false, 800), new Prato("peixe", false, 400), new Prato("salada", true, 150), new Prato("macarrão", true, 550) );
// Agrupar por vegetarianismo e contar itens
Map<Boolean, Long> contagemVegetarianos = cardapio.stream()
.collect(Collectors.partitioningBy(
Prato::vegetariano,
Collectors.counting()
));
// Agrupar por tipo e calcular média calórica
Map<String, Double> mediaCaloriasPorTipo = cardapio.stream()
.collect(Collectors.groupingBy(
p -> p.vegetariano() ? "Vegetariano" : "Não-vegetariano",
Collectors.averagingInt(Prato::calorias)
));
System.out.println("Contagem vegetarianos: " + contagemVegetarianos);
System.out.println("Média por tipo: " + mediaCaloriasPorTipo);
}
}
</div>### Manipulação de Strings e Coleções
Operações de concatenação e transformação de coleções são simplificadas com os collectors.
<div class="code-example">```
package com.example.streams;
import java.util.*;
import java.util.stream.Collectors;
public class TransformacaoCollector {
public static void main(String[] args) {
List<String> nomes = Arrays.asList("Ana", "Bruno", "Carlos", "Diana");
// Concatenar strings com delimitador
String nomesConcatenados = nomes.stream()
.collect(Collectors.joining(", ", "[", "]"));
// Converter para conjunto
Set<String> conjuntoNomes = nomes.stream()
.collect(Collectors.toSet());
// Mapear para nova estrutura usando mapping
String iniciais = nomes.stream()
.collect(Collectors.mapping(
nome -> String.valueOf(nome.charAt(0)),
Collectors.joining("", "Iniciais: ", "")
));
System.out.println("Nomes concatenados: " + nomesConcatenados);
System.out.println("Conjunto: " + conjuntoNomes);
System.out.println(iniciais);
}
}
Podemos direcionar a coleta para impleemntações específicas de coleções.
package com.example.streams;
import java.util.*; import java.util.concurrent.ConcurrentHashMap; import java.util.stream.Collectors;
public class ColecoesEspecificas { public static void main(String[] args) { List<Integer> numeros = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9);
// Coletar em LinkedList
LinkedList<Integer> listaEncadeada = numeros.stream()
.filter(n -> n % 2 == 0)
.collect(Collectors.toCollection(LinkedList::new));
// Criar mapa concorrente
ConcurrentHashMap<String, Integer> mapaConcorrente = numeros.stream()
.collect(Collectors.toConcurrentMap(
n -> "chave_" + n,
n -> n * 10,
(v1, v2) -> v1 + v2
));
// Usar reducing para operações personalizadas
int somaTotal = numeros.stream()
.collect(Collectors.reducing(0, Integer::sum));
System.out.println("Lista encadeada: " + listaEncadeada);
System.out.println("Mapa concorrente: " + mapaConcorrente);
System.out.println("Soma total: " + somaTotal);
}
}
</div>A flexibilidade da API de Collectors permite construir operações complexas de processamento de dados de maneira decalrativa e concisa, aproveitando todo o poder do framework de Stream do Java.