0Pricing
Java Academy · Aula

Construção de um Collector Personalizado

Implemente a interface Collector para criar uma agregação reutilizável voltada a um caso de uso específico do domínio.

Construção de um Collector Personalizado é uma aula grátis de Java Academy no CoddyKit. Esta é a aula 4 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Java Academy, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Java Academy inclui 4 aulas no total.

Por que criar um coletor personalizado?

Quando os coletores integrados não atendem ao seu domínio, implemente a interface Collector<T, A, R> para criar uma lógica de agregação reutilizável e combinável. T=entrada, A=acumulador mutável, R=resultado.

A interface Collector

Um coletor tem cinco componentes:

  • supplier() — cria o acumulador inicial
  • accumulator() — incorpora um elemento ao acumulador
  • combiner() — combina dois acumuladores para fluxos paralelos
  • finisher() — transforma o acumulador no resultado final
  • characteristics() — fornece indicações para otimização

Fábrica Collector.of()

A maneira mais simples de criar um coletor personalizado é usar Collector.of() sem implementar a interface diretamente:

import java.util.stream.*;

// Collector that builds a comma-separated String
Collector<String, StringBuilder, String> csvCollector = Collector.of(
    StringBuilder::new,                     // supplier
    (sb, s) -> {
        if (sb.length() > 0) sb.append(',');
        sb.append(s);
    },                                       // accumulator
    (a, b) -> {
        if (a.length() > 0 && b.length() > 0) a.append(',');
        a.append(b); return a;
    },                                       // combiner
    StringBuilder::toString                  // finisher
);

String csv = Stream.of("Alice","Bob","Carol").collect(csvCollector);
System.out.println(csv); // Alice,Bob,Carol

Coletor personalizado: TopN

Crie um coletor que mantenha apenas os N elementos principais de acordo com um comparador:

import java.util.*;
import java.util.stream.*;

static <T> Collector<T, ?, List<T>> topN(int n, Comparator<T> comp) {
    return Collector.of(
        () -> new ArrayList<T>(),
        (list, e) -> {
            list.add(e);
            list.sort(comp.reversed());
            if (list.size() > n) list.remove(list.size() - 1);
        },
        (a, b) -> {
            a.addAll(b);
            a.sort(comp.reversed());
            while (a.size() > n) a.remove(a.size() - 1);
            return a;
        },
        Collector.Characteristics.UNORDERED
    );
}

Usando o coletor TopN

Aplique o coletor TopN personalizado a um fluxo:

List<Integer> top3 = Stream.of(5,2,9,1,7,3,8)
    .collect(topN(3, Comparator.naturalOrder()));
System.out.println(top3); // [9, 8, 7]

Sinalizadores de características

Três características opcionais ajustam a otimização do fluxo:

  • IDENTITY_FINISH — o finalizador é a identidade; ignore-o
  • UNORDERED — a ordem do resultado não importa
  • CONCURRENT — o acumulador pode ser chamado simultaneamente

Definir o sinalizador errado, como CONCURRENT sem segurança para uso simultâneo, causa erros.

Coletor personalizado: média acumulada

Mantenha uma média acumulada sem fazer uma segunda passagem:

record RunningAvg(long count, double sum) {
    RunningAvg add(double v) { return new RunningAvg(count+1, sum+v); }
    RunningAvg merge(RunningAvg o) { return new RunningAvg(count+o.count, sum+o.sum); }
    double avg() { return count == 0 ? 0 : sum / count; }
}

Collector<Double, RunningAvg[], Double> avgCollector = Collector.of(
    () -> new RunningAvg[]{new RunningAvg(0, 0)},
    (a, v) -> a[0] = a[0].add(v),
    (a, b) -> { a[0] = a[0].merge(b[0]); return a; },
    a -> a[0].avg()
);

double avg = Stream.of(10.0, 20.0, 30.0).collect(avgCollector);
System.out.println(avg); // 20.0

Implementando diretamente a interface Collector

Para ter mais controle, implemente Collector<T,A,R> como uma classe:

import java.util.function.*;
import java.util.stream.*;

class StringJoiningCollector implements Collector<String, List<String>, String> {
    private final String delimiter;
    StringJoiningCollector(String delimiter) { this.delimiter = delimiter; }
    public Supplier<List<String>> supplier() { return ArrayList::new; }
    public BiConsumer<List<String>, String> accumulator() { return List::add; }
    public BinaryOperator<List<String>> combiner() {
        return (a,b) -> { a.addAll(b); return a; };
    }
    public Function<List<String>, String> finisher() {
        return list -> String.join(delimiter, list);
    }
    public Set<Characteristics> characteristics() { return Set.of(); }
}

Comparando coletores personalizados e integrados

Antes de escrever um coletor personalizado, verifique se a composição de coletores integrados produz o mesmo resultado — eles já foram testados e otimizados:

// Instead of custom, compose:
Map<String, IntSummaryStatistics> stats = orders.stream().collect(
    Collectors.groupingBy(Order::status,
        Collectors.summarizingInt(Order::amount))
);
// This replaces many custom collector implementations

Segurança de uso simultâneo em coletores personalizados

Se a característica CONCURRENT estiver definida, o acumulador será chamado simultaneamente por várias linhas de execução. Nesse caso, o acumulador deverá ser seguro para uso simultâneo, por exemplo, usando ConcurrentHashMap ou AtomicLong. Sem CONCURRENT, o combiner trata o paralelismo com segurança.

Caso real: coletor de histograma

Agrupe valores numéricos em faixas dentro de um mapa de histograma:

Collector<Integer, Map<Integer, Long>, Map<Integer, Long>> histogram =
    Collector.of(
        LinkedHashMap::new,
        (map, val) -> map.merge(val / 10 * 10, 1L, Long::sum),
        (a, b) -> { b.forEach((k,v) -> a.merge(k,v,Long::sum)); return a; },
        Collector.Characteristics.IDENTITY_FINISH
    );

Map<Integer,Long> hist = Stream.of(5,15,25,12,8,22,18,3)
    .collect(histogram);
hist.forEach((bucket, cnt) -> System.out.println(bucket+"-"+(bucket+9)+": "+cnt));

Verificação rápida

Qual sinalizador de características do coletor indica que a função de finalização é a transformação identidade e pode ser ignorada?

Recapitulação: coletores personalizados

Principais pontos:

  • A interface do coletor usa T como entrada, A como acumulador e R como resultado
  • Use Collector.of() para uma implementação rápida sem criar uma classe
  • supplier, accumulator, combiner, finisher e characteristics são as cinco partes
  • As características de finalização por identidade, ausência de ordem e concorrência são sinalizadores de otimização
  • Dê preferência à composição de coletores integrados; crie um personalizado apenas quando necessário

Perguntas Frequentes

A aula “Construção de um Collector Personalizado” é grátis?

Sim — o texto completo de “Construção de um Collector Personalizado” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Java Academy, atualize para CoddyKit PRO. O curso de Java Academy inclui 4 aulas no total.

O que vou aprender em “Construção de um Collector Personalizado”?

Implemente a interface Collector para criar uma agregação reutilizável voltada a um caso de uso específico do domínio. Você pratica Java Academy com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.

Preciso ter experiência prévia para começar Java Academy?

Nenhuma experiência prévia é necessária. Java Academy no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 4 de 4.

Quanto tempo leva a aula “Construção de um Collector Personalizado”?

A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.

Posso escrever e executar código nesta aula de Java Academy?

Sim. Cada aula de Java Academy inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.

Todas as aulas deste curso

  1. groupingBy: Classificação de Elementos
  2. partitioningBy e Contagem
  3. toMap, joining e summarizing
  4. Construção de um Collector Personalizado
← Voltar para Java Academy