Pular para conteúdo

Java Streams

Um stream descreve um pipeline sobre elementos. Não é uma coleção e não armazena seus próprios elementos. Um pipeline contém uma fonte, zero ou mais operações intermediárias e uma operação terminal.

Preguiça e único uso

Operações intermediárias como map e filter são avaliadas tardiamente; a travessia começa quando uma operação terminal solicita resultados. Um stream não pode ser reutilizado após uma operação terminal. O guia mais amplo sobre processamento tardio e imediato cobre avaliação, ciclo de vida de recursos, snapshots e perigos de persistência.

record Reading(String sensor, double value) {}

List<String> activeSensors = readings.stream()
        .filter(reading -> reading.value() > 0.0)
        .map(Reading::sensor)
        .distinct()
        .sorted()
        .toList();

A fonte não é modificada, mas a não interferência é responsabilidade do chamador: alterar a fonte durante a travessia ou alterar o estado compartilhado pelas operações pode tornar o comportamento inseguro ou não determinístico.

Categorias de operação

Tipo Exemplos Resultado
Intermediário sem estado map, filter Outro stream
Intermediário com estado distinct, sorted, limit Outro stream, possivelmente armazenado em buffer
Curto-circuito findFirst, anyMatch, limit Pode terminar cedo
Redução terminal reduce, collect, count Resultado não-stream

peek é principalmente um ponto de observação; não faça a correção depender de seus efeitos colaterais, pois otimizações e curto-circuitos afetam quais elementos são observados.

Redução

Map<String, Double> maximumBySensor = readings.stream()
        .collect(Collectors.toMap(
                Reading::sensor,
                Reading::value,
                Math::max));

Para uma redução paralela, a identidade e o acumulador/combinador devem satisfazer os requisitos de compatibilidade algébrica documentados. Associatividade é essencial; a adição de ponto flutuante não é matematicamente associativa; portanto, o reagrupamento pode mudar o arredondamento.

Streams paralelos

Paralelismo não é uma chave de velocidade gratuita. Funciona melhor para cargas suficientemente grandes, limitadas pela CPU, divisíveis, com operações sem estado e combinação barata. I/O bloqueante, mutação compartilhada, restrições de ordem de encontro, pequenas entradas e contenção podem torná-lo mais lento ou inseguro. Meça com um benchmark representativo.

Exercícios

  1. Substituir um stream aninhado por flatMap.
  2. Explicar diferença entre findFirst e findAny em paralelo.
  3. Escreva um coletor que agrupe leituras por sensor e calcule estatísticas resumidas.